usagi-picker 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. usagi_picker-0.1.0/LICENSE +21 -0
  2. usagi_picker-0.1.0/PKG-INFO +72 -0
  3. usagi_picker-0.1.0/README.md +49 -0
  4. usagi_picker-0.1.0/pyproject.toml +45 -0
  5. usagi_picker-0.1.0/setup.cfg +4 -0
  6. usagi_picker-0.1.0/src/usagi_picker/__init__.py +1 -0
  7. usagi_picker-0.1.0/src/usagi_picker/constants.py +119 -0
  8. usagi_picker-0.1.0/src/usagi_picker/core/__init__.py +19 -0
  9. usagi_picker-0.1.0/src/usagi_picker/core/actions/__init__.py +73 -0
  10. usagi_picker-0.1.0/src/usagi_picker/core/actions/abstract.py +68 -0
  11. usagi_picker-0.1.0/src/usagi_picker/core/actions/context_menu.py +55 -0
  12. usagi_picker-0.1.0/src/usagi_picker/core/actions/deselect.py +22 -0
  13. usagi_picker-0.1.0/src/usagi_picker/core/actions/key_all.py +15 -0
  14. usagi_picker-0.1.0/src/usagi_picker/core/actions/key_customs.py +20 -0
  15. usagi_picker-0.1.0/src/usagi_picker/core/actions/key_rotate.py +15 -0
  16. usagi_picker-0.1.0/src/usagi_picker/core/actions/key_scale.py +15 -0
  17. usagi_picker-0.1.0/src/usagi_picker/core/actions/key_translate.py +15 -0
  18. usagi_picker-0.1.0/src/usagi_picker/core/actions/match_ik_fk.py +16 -0
  19. usagi_picker-0.1.0/src/usagi_picker/core/actions/mirror.py +19 -0
  20. usagi_picker-0.1.0/src/usagi_picker/core/actions/reset_all.py +19 -0
  21. usagi_picker-0.1.0/src/usagi_picker/core/actions/reset_customs.py +19 -0
  22. usagi_picker-0.1.0/src/usagi_picker/core/actions/reset_rotate.py +21 -0
  23. usagi_picker-0.1.0/src/usagi_picker/core/actions/reset_scale.py +21 -0
  24. usagi_picker-0.1.0/src/usagi_picker/core/actions/reset_translate.py +21 -0
  25. usagi_picker-0.1.0/src/usagi_picker/core/actions/select.py +22 -0
  26. usagi_picker-0.1.0/src/usagi_picker/core/actions/select_add.py +22 -0
  27. usagi_picker-0.1.0/src/usagi_picker/core/actions/select_opposite.py +16 -0
  28. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_asset.py +19 -0
  29. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_controllers.py +24 -0
  30. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_nodes_visibility.py +17 -0
  31. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_shapes_visibility.py +18 -0
  32. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_smooth.py +20 -0
  33. usagi_picker-0.1.0/src/usagi_picker/core/actions/toggle_textures.py +32 -0
  34. usagi_picker-0.1.0/src/usagi_picker/core/graph.py +118 -0
  35. usagi_picker-0.1.0/src/usagi_picker/core/picker_data_node.py +11 -0
  36. usagi_picker-0.1.0/src/usagi_picker/core/widgets/__init__.py +17 -0
  37. usagi_picker-0.1.0/src/usagi_picker/core/widgets/abstract.py +57 -0
  38. usagi_picker-0.1.0/src/usagi_picker/core/widgets/backdrop.py +11 -0
  39. usagi_picker-0.1.0/src/usagi_picker/core/widgets/button.py +16 -0
  40. usagi_picker-0.1.0/src/usagi_picker/core/widgets/folder.py +16 -0
  41. usagi_picker-0.1.0/src/usagi_picker/core/widgets/label.py +16 -0
  42. usagi_picker-0.1.0/src/usagi_picker/core/widgets/select_shape.py +16 -0
  43. usagi_picker-0.1.0/src/usagi_picker/core/widgets/settings.py +27 -0
  44. usagi_picker-0.1.0/src/usagi_picker/core/widgets/shape.py +31 -0
  45. usagi_picker-0.1.0/src/usagi_picker/core/widgets/tab.py +11 -0
  46. usagi_picker-0.1.0/src/usagi_picker/core/widgets/toggle.py +24 -0
  47. usagi_picker-0.1.0/src/usagi_picker/editor/__init__.py +12 -0
  48. usagi_picker-0.1.0/src/usagi_picker/editor/core/__init__.py +1 -0
  49. usagi_picker-0.1.0/src/usagi_picker/editor/core/alignments.py +71 -0
  50. usagi_picker-0.1.0/src/usagi_picker/editor/core/picker_scene_items.py +358 -0
  51. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/__init__.py +98 -0
  52. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/abstract.py +187 -0
  53. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/backdrop.py +17 -0
  54. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/button.py +21 -0
  55. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/folder.py +21 -0
  56. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/label.py +21 -0
  57. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/select_shape.py +17 -0
  58. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/settings.py +50 -0
  59. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/shape.py +106 -0
  60. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/tab.py +16 -0
  61. usagi_picker-0.1.0/src/usagi_picker/editor/core/widgets/toggle.py +39 -0
  62. usagi_picker-0.1.0/src/usagi_picker/editor/picker_editor.py +890 -0
  63. usagi_picker-0.1.0/src/usagi_picker/editor/widgets/__init__.py +3 -0
  64. usagi_picker-0.1.0/src/usagi_picker/editor/widgets/action_arg_widget.py +105 -0
  65. usagi_picker-0.1.0/src/usagi_picker/editor/widgets/action_edit_widget.py +234 -0
  66. usagi_picker-0.1.0/src/usagi_picker/editor/widgets/edit_modifier_widget.py +53 -0
  67. usagi_picker-0.1.0/src/usagi_picker/maya_utils/__init__.py +1 -0
  68. usagi_picker-0.1.0/src/usagi_picker/maya_utils/axis.py +68 -0
  69. usagi_picker-0.1.0/src/usagi_picker/maya_utils/color.py +72 -0
  70. usagi_picker-0.1.0/src/usagi_picker/maya_utils/contexts.py +22 -0
  71. usagi_picker-0.1.0/src/usagi_picker/maya_utils/data/__init__.py +10 -0
  72. usagi_picker-0.1.0/src/usagi_picker/maya_utils/decorators.py +43 -0
  73. usagi_picker-0.1.0/src/usagi_picker/maya_utils/exceptions.py +9 -0
  74. usagi_picker-0.1.0/src/usagi_picker/maya_utils/ik_fk.py +237 -0
  75. usagi_picker-0.1.0/src/usagi_picker/maya_utils/maya_enum_values.py +72 -0
  76. usagi_picker-0.1.0/src/usagi_picker/maya_utils/mirror.py +87 -0
  77. usagi_picker-0.1.0/src/usagi_picker/maya_utils/naming.py +127 -0
  78. usagi_picker-0.1.0/src/usagi_picker/maya_utils/nodes.py +23 -0
  79. usagi_picker-0.1.0/src/usagi_picker/maya_utils/nurbs.py +170 -0
  80. usagi_picker-0.1.0/src/usagi_picker/maya_utils/omutils.py +24 -0
  81. usagi_picker-0.1.0/src/usagi_picker/maya_utils/orig.py +67 -0
  82. usagi_picker-0.1.0/src/usagi_picker/maya_utils/sets.py +13 -0
  83. usagi_picker-0.1.0/src/usagi_picker/maya_utils/textures.py +20 -0
  84. usagi_picker-0.1.0/src/usagi_picker/maya_utils/transforms.py +21 -0
  85. usagi_picker-0.1.0/src/usagi_picker/picker/__init__.py +18 -0
  86. usagi_picker-0.1.0/src/usagi_picker/picker/dialogs/__init__.py +1 -0
  87. usagi_picker-0.1.0/src/usagi_picker/picker/dialogs/select_asset_dialog.py +119 -0
  88. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/__init__.py +1 -0
  89. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/items/__init__.py +15 -0
  90. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/items/abstract.py +89 -0
  91. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/items/backdrop.py +30 -0
  92. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/items/select_shape.py +59 -0
  93. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/items/shape.py +21 -0
  94. usagi_picker-0.1.0/src/usagi_picker/picker/graphics/picker_graphic_view.py +119 -0
  95. usagi_picker-0.1.0/src/usagi_picker/picker/usagi_picker.py +122 -0
  96. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/__init__.py +1 -0
  97. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/main_widget.py +230 -0
  98. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/picker_tab_widget.py +71 -0
  99. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/toolbar_button.py +89 -0
  100. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/toolbar_folder.py +20 -0
  101. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/toolbar_label.py +15 -0
  102. usagi_picker-0.1.0/src/usagi_picker/picker/widgets/toolbar_toggle_switch.py +27 -0
  103. usagi_picker-0.1.0/src/usagi_picker/qsagi/__init__.py +1 -0
  104. usagi_picker-0.1.0/src/usagi_picker/qsagi/dialogs/__init__.py +1 -0
  105. usagi_picker-0.1.0/src/usagi_picker/qsagi/maya_utils.py +11 -0
  106. usagi_picker-0.1.0/src/usagi_picker/qsagi/qt.py +106 -0
  107. usagi_picker-0.1.0/src/usagi_picker/qsagi/utils.py +115 -0
  108. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/__init__.py +1 -0
  109. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/array_widget.py +168 -0
  110. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/blend_widget.py +166 -0
  111. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/flow_layout.py +88 -0
  112. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/folder.py +156 -0
  113. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/maya_node_selector.py +100 -0
  114. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/maya_viewport_widget.py +89 -0
  115. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/path_line_edit.py +86 -0
  116. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/rules.py +17 -0
  117. usagi_picker-0.1.0/src/usagi_picker/qsagi/widgets/toggle_switch.py +93 -0
  118. usagi_picker-0.1.0/src/usagi_picker/resources/__init__.py +8 -0
  119. usagi_picker-0.1.0/src/usagi_picker/resources/icons/3d-cube.png +0 -0
  120. usagi_picker-0.1.0/src/usagi_picker/resources/icons/add-dark.png +0 -0
  121. usagi_picker-0.1.0/src/usagi_picker/resources/icons/add.png +0 -0
  122. usagi_picker-0.1.0/src/usagi_picker/resources/icons/align-center.png +0 -0
  123. usagi_picker-0.1.0/src/usagi_picker/resources/icons/checked.png +0 -0
  124. usagi_picker-0.1.0/src/usagi_picker/resources/icons/click.png +0 -0
  125. usagi_picker-0.1.0/src/usagi_picker/resources/icons/close-hover.png +0 -0
  126. usagi_picker-0.1.0/src/usagi_picker/resources/icons/close.png +0 -0
  127. usagi_picker-0.1.0/src/usagi_picker/resources/icons/collapse.png +0 -0
  128. usagi_picker-0.1.0/src/usagi_picker/resources/icons/copy.png +0 -0
  129. usagi_picker-0.1.0/src/usagi_picker/resources/icons/down_arrow.png +0 -0
  130. usagi_picker-0.1.0/src/usagi_picker/resources/icons/duplicate.png +0 -0
  131. usagi_picker-0.1.0/src/usagi_picker/resources/icons/expand.png +0 -0
  132. usagi_picker-0.1.0/src/usagi_picker/resources/icons/flip.png +0 -0
  133. usagi_picker-0.1.0/src/usagi_picker/resources/icons/folder-white.png +0 -0
  134. usagi_picker-0.1.0/src/usagi_picker/resources/icons/format.png +0 -0
  135. usagi_picker-0.1.0/src/usagi_picker/resources/icons/office-push-pin.png +0 -0
  136. usagi_picker-0.1.0/src/usagi_picker/resources/icons/paste.png +0 -0
  137. usagi_picker-0.1.0/src/usagi_picker/resources/icons/reload.png +0 -0
  138. usagi_picker-0.1.0/src/usagi_picker/resources/icons/run.png +0 -0
  139. usagi_picker-0.1.0/src/usagi_picker/resources/icons/trashbin.png +0 -0
  140. usagi_picker-0.1.0/src/usagi_picker/resources/icons/up_arrow.png +0 -0
  141. usagi_picker-0.1.0/src/usagi_picker/resources/styles/style.qss +520 -0
  142. usagi_picker-0.1.0/src/usagi_picker/tools/__init__.py +0 -0
  143. usagi_picker-0.1.0/src/usagi_picker/tools/mirror_tool/__init__.py +10 -0
  144. usagi_picker-0.1.0/src/usagi_picker/tools/mirror_tool/ui.py +410 -0
  145. usagi_picker-0.1.0/src/usagi_picker/tools/nurbs_shape_tool/__init__.py +10 -0
  146. usagi_picker-0.1.0/src/usagi_picker/tools/nurbs_shape_tool/ui.py +550 -0
  147. usagi_picker-0.1.0/src/usagi_picker.egg-info/PKG-INFO +72 -0
  148. usagi_picker-0.1.0/src/usagi_picker.egg-info/SOURCES.txt +148 -0
  149. usagi_picker-0.1.0/src/usagi_picker.egg-info/dependency_links.txt +1 -0
  150. usagi_picker-0.1.0/src/usagi_picker.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MF_ZOOL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: usagi-picker
3
+ Version: 0.1.0
4
+ Summary: Animation picker for Autodesk Maya
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/zool-rig/usagi-picker
7
+ Project-URL: Repository, https://github.com/zool-rig/usagi-picker
8
+ Project-URL: Documentation, https://github.com/zool-rig/usagi-picker/tree/main/python/bozon-ui
9
+ Project-URL: Issues, https://github.com/zool-rig/usagi-picker/issues
10
+ Keywords: animation,maya,picker,rig,usagi,anim
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Topic :: Multimedia :: Graphics
18
+ Classifier: Topic :: Software Development :: User Interfaces
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # Usagi Picker 🐇
25
+
26
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
27
+ [![Python](https://img.shields.io/badge/Python-3.7%2B-blue.svg)](https://www.python.org)
28
+ [![Maya](https://img.shields.io/badge/Maya-2022%2B-green.svg)](https://www.autodesk.com/products/maya/overview)
29
+ [![PyPI version](https://badge.fury.io/py/usagi-picker.svg)](https://badge.fury.io/py/usagi-picker)
30
+
31
+ ---
32
+
33
+ ## Table of Contents
34
+
35
+ - [Installation](#installation)
36
+ - [Features](#features)
37
+ - [Usage](#usage)
38
+ - [Project Status](#project-status)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
41
+
42
+ ---
43
+
44
+ Usagi is a modern open source animation picker for Autodesk Maya.
45
+
46
+ ![picker image](/doc/images/picker.png)
47
+
48
+ ## Installation
49
+
50
+ Clone this repository and drop the `drag_n_drop_install.py` file in the maya's viewport.
51
+
52
+ Or install it with pip
53
+
54
+ ```bash
55
+ mayapy -m pip install usagi-picker
56
+ ```
57
+
58
+ ## Features
59
+
60
+ * Create a picker for any rigs using maya objects as templates.
61
+
62
+ ![edit scene image](/doc/images/edit_scene.png)
63
+
64
+ * Navigate in the picker like in a 2D scene.
65
+
66
+ ## Usage
67
+
68
+ ## Project Status
69
+
70
+ ## Contributing
71
+
72
+ ## License
@@ -0,0 +1,49 @@
1
+ # Usagi Picker 🐇
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+ [![Python](https://img.shields.io/badge/Python-3.7%2B-blue.svg)](https://www.python.org)
5
+ [![Maya](https://img.shields.io/badge/Maya-2022%2B-green.svg)](https://www.autodesk.com/products/maya/overview)
6
+ [![PyPI version](https://badge.fury.io/py/usagi-picker.svg)](https://badge.fury.io/py/usagi-picker)
7
+
8
+ ---
9
+
10
+ ## Table of Contents
11
+
12
+ - [Installation](#installation)
13
+ - [Features](#features)
14
+ - [Usage](#usage)
15
+ - [Project Status](#project-status)
16
+ - [Contributing](#contributing)
17
+ - [License](#license)
18
+
19
+ ---
20
+
21
+ Usagi is a modern open source animation picker for Autodesk Maya.
22
+
23
+ ![picker image](/doc/images/picker.png)
24
+
25
+ ## Installation
26
+
27
+ Clone this repository and drop the `drag_n_drop_install.py` file in the maya's viewport.
28
+
29
+ Or install it with pip
30
+
31
+ ```bash
32
+ mayapy -m pip install usagi-picker
33
+ ```
34
+
35
+ ## Features
36
+
37
+ * Create a picker for any rigs using maya objects as templates.
38
+
39
+ ![edit scene image](/doc/images/edit_scene.png)
40
+
41
+ * Navigate in the picker like in a 2D scene.
42
+
43
+ ## Usage
44
+
45
+ ## Project Status
46
+
47
+ ## Contributing
48
+
49
+ ## License
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "usagi-picker"
3
+ version = "0.1.0"
4
+ description = "Animation picker for Autodesk Maya"
5
+ readme = "README.md"
6
+ requires-python = ">=3.7"
7
+ license = { text = "MIT" }
8
+ keywords = ["animation", "maya", "picker", "rig", "usagi", "anim"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Intended Audience :: End Users/Desktop",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3 :: Only",
15
+ "Programming Language :: Python :: 3.7",
16
+ "Topic :: Multimedia :: Graphics",
17
+ "Topic :: Software Development :: User Interfaces",
18
+ ]
19
+ dependencies = []
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/zool-rig/usagi-picker"
23
+ Repository = "https://github.com/zool-rig/usagi-picker"
24
+ Documentation = "https://github.com/zool-rig/usagi-picker/tree/main/python/bozon-ui"
25
+ Issues = "https://github.com/zool-rig/usagi-picker/issues"
26
+
27
+ [build-system]
28
+ requires = ["setuptools>=61.0"]
29
+ build-backend = "setuptools.build_meta"
30
+
31
+ [tool.setuptools]
32
+ include-package-data = true
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.setuptools.package-data]
38
+ usagi_picker = [
39
+ "resources/styles/*.qss",
40
+ "resources/icons/*",
41
+ "resources/icons/**/*",
42
+ ]
43
+
44
+ [tool.ruff.lint.per-file-ignores]
45
+ "**/__init__.py" = ["F401"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from usagi_picker.maya_utils.color import Color
6
+
7
+
8
+ # Naming convention
9
+ VALID_NAME_CHARS: str = r"^[a-zA-Z0-9_:]+$"
10
+ SIDE_MIRROR_MAP: dict[str, str] = {
11
+ "L": "R",
12
+ "R": "L",
13
+ "C": "C"
14
+ }
15
+
16
+ # Suffixes
17
+ ORIG_SUFFIX: str = "orig"
18
+ GROUP_SUFFIX: str = "grp"
19
+ PICKER_SUFFIX: str = "pkr"
20
+
21
+ # Groups
22
+ ROOT_GROUP: str = f"root__{GROUP_SUFFIX}"
23
+ PICKER_GROUP: str = f"picker__{GROUP_SUFFIX}"
24
+ SETTINGS_NODE = f"settings__{GROUP_SUFFIX}"
25
+
26
+ # Attributes
27
+ PICKER_DATA_ATTRIBUTE: str = "usagi_picker_data"
28
+ IS_TAB_TAG: str = "usagi_picker_is_tab"
29
+ IS_BACKDROP_TAG: str = "usagi_picker_is_backdrop"
30
+ IS_SETTINGS_TAG: str = "usagi_picker_is_settings"
31
+ IS_SELECT_SHAPE_TAG: str = "usagi_picker_is_select_shape"
32
+ IS_BUTTON_TAG: str = "usagi_picker_is_button"
33
+ IS_LABEL_TAG: str = "usagi_picker_is_label"
34
+ IS_TOGGLE_TAG: str = "usagi_picker_is_toggle"
35
+ IS_FOLDER_TAG: str = "usagi_picker_is_folder"
36
+ ACTION_TAG: str = "usagi_picker_actions"
37
+ TEXT_TAG: str = "usagi_picker_text"
38
+ COLOR_TAG: str = "usagi_picker_color"
39
+ OUTLINE_COLOR_TAG: str = "usagi_picker_outline_color"
40
+ SELCTION_COLOR_TAG: str = "usagi_picker_selection_color"
41
+ MIRROR_ATTRIBUTE = "mirror_behavior"
42
+
43
+ # Misc
44
+ PICKER_EXTENSION: str = ".upkr"
45
+ IDENTITY_MATRIX: list[float] = [
46
+ 1.0,
47
+ 0.0,
48
+ 0.0,
49
+ 0.0,
50
+ 0.0,
51
+ 1.0,
52
+ 0.0,
53
+ 0.0,
54
+ 0.0,
55
+ 0.0,
56
+ 1.0,
57
+ 0.0,
58
+ 0.0,
59
+ 0.0,
60
+ 0.0,
61
+ 1.0,
62
+ ]
63
+ SHAPE_DEFAULT_RADIUS: int = 30
64
+ SHAPE_DEFAULT_COLOR: Color = Color(0, 1, 0)
65
+ SELECT_SHAPE_DEFAULT_ACTIONS: dict[str, Any] = {
66
+ "1": {"name": "Select", "args": [[]], "mandatory": True},
67
+ "2": {
68
+ "name": "Context Menu",
69
+ "args": [
70
+ [],
71
+ [
72
+ "Toggle Nodes Visibility",
73
+ "Toggle Shapes Visibility",
74
+ "Separator",
75
+ "Reset All",
76
+ "Reset Translate",
77
+ "Reset Rotate",
78
+ "Reset Scale",
79
+ "Reset Customs",
80
+ "Separator",
81
+ "Mirror",
82
+ # "Select Opposite",
83
+ "Separator",
84
+ "Key All",
85
+ "Key Translate",
86
+ "Key Rotate",
87
+ "Key Scale",
88
+ "Key Customs",
89
+ ],
90
+ ],
91
+ "mandatory": False,
92
+ },
93
+ "67108866": {
94
+ "name": "Context Menu",
95
+ "args": [
96
+ [],
97
+ [
98
+ "Toggle Nodes Visibility",
99
+ "Toggle Shapes Visibility",
100
+ "Separator",
101
+ "Reset All",
102
+ "Reset Translate",
103
+ "Reset Rotate",
104
+ "Reset Scale",
105
+ "Reset Customs",
106
+ "Separator",
107
+ "Mirror",
108
+ # "Select Opposite",
109
+ "Separator",
110
+ "Key All",
111
+ "Key Translate",
112
+ "Key Rotate",
113
+ "Key Scale",
114
+ "Key Customs",
115
+ ],
116
+ ],
117
+ "mandatory": False,
118
+ },
119
+ }
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.constants import PICKER_DATA_ATTRIBUTE
6
+
7
+ from usagi_picker.core.picker_data_node import PickerDataNode
8
+ from usagi_picker.core.graph import Graph
9
+
10
+
11
+ def collect_pickers() -> list[PickerDataNode]:
12
+ picker_data_nodes = []
13
+ picker_data_plugs = cmds.ls(f"::*.{PICKER_DATA_ATTRIBUTE}")
14
+ for plug in sorted(picker_data_plugs):
15
+ namespace = plug.rpartition(":")[0]
16
+ data = cmds.getAttr(plug)
17
+ graph = Graph.loads(data)
18
+ picker_data_nodes.append(PickerDataNode(namespace, graph))
19
+ return picker_data_nodes
@@ -0,0 +1,73 @@
1
+ from json import JSONDecoder, JSONEncoder
2
+ from typing import Any
3
+
4
+ from usagi_picker.core.actions.abstract import AbstractAction
5
+ from usagi_picker.core.actions.select import SelectAction
6
+ from usagi_picker.core.actions.select_add import SelectAddAction
7
+ from usagi_picker.core.actions.deselect import DeselectAction
8
+ from usagi_picker.core.actions.context_menu import ContextMenuAction
9
+ from usagi_picker.core.actions.reset_all import ResetAll
10
+ from usagi_picker.core.actions.reset_translate import ResetTranslate
11
+ from usagi_picker.core.actions.reset_rotate import ResetRotate
12
+ from usagi_picker.core.actions.reset_scale import ResetScale
13
+ from usagi_picker.core.actions.reset_customs import ResetCustoms
14
+ from usagi_picker.core.actions.toggle_textures import ToggleTextures
15
+ from usagi_picker.core.actions.toggle_smooth import ToggleSmooth
16
+ from usagi_picker.core.actions.toggle_controllers import ToggleControllers
17
+ from usagi_picker.core.actions.mirror import Mirror
18
+ from usagi_picker.core.actions.select_opposite import SelectOpposite
19
+ from usagi_picker.core.actions.key_all import KeyAll
20
+ from usagi_picker.core.actions.key_translate import KeyTranslate
21
+ from usagi_picker.core.actions.key_rotate import KeyRotate
22
+ from usagi_picker.core.actions.key_scale import KeyScale
23
+ from usagi_picker.core.actions.key_customs import KeyCustoms
24
+ from usagi_picker.core.actions.toggle_asset import ToggleAsset
25
+ from usagi_picker.core.actions.toggle_nodes_visibility import ToogleNodesVisibility
26
+ from usagi_picker.core.actions.toggle_shapes_visibility import ToogleShapesVisibility
27
+ from usagi_picker.core.actions.match_ik_fk import MatchIKFKAction
28
+
29
+ ACTIONS = {
30
+ SelectAction.NAME: SelectAction,
31
+ SelectAddAction.NAME: SelectAddAction,
32
+ DeselectAction.NAME: DeselectAction,
33
+ ContextMenuAction.NAME: ContextMenuAction,
34
+ ResetAll.NAME: ResetAll,
35
+ ResetTranslate.NAME: ResetTranslate,
36
+ ResetRotate.NAME: ResetRotate,
37
+ ResetScale.NAME: ResetScale,
38
+ ResetCustoms.NAME: ResetCustoms,
39
+ ToggleTextures.NAME: ToggleTextures,
40
+ ToggleSmooth.NAME: ToggleSmooth,
41
+ ToggleControllers.NAME: ToggleControllers,
42
+ Mirror.NAME: Mirror,
43
+ SelectOpposite.NAME: SelectOpposite,
44
+ KeyAll.NAME: KeyAll,
45
+ KeyTranslate.NAME: KeyTranslate,
46
+ KeyRotate.NAME: KeyRotate,
47
+ KeyScale.NAME: KeyScale,
48
+ KeyCustoms.NAME: KeyCustoms,
49
+ ToggleAsset.NAME: ToggleAsset,
50
+ ToogleNodesVisibility.NAME: ToogleNodesVisibility,
51
+ ToogleShapesVisibility.NAME: ToogleShapesVisibility,
52
+ MatchIKFKAction.NAME: MatchIKFKAction,
53
+ }
54
+
55
+
56
+ class ActionEncoder(JSONEncoder):
57
+ def default(self, o: Any) -> Any:
58
+ if isinstance(o, AbstractAction):
59
+ return o.serialize()
60
+ return super().default(o)
61
+
62
+
63
+ class ActionDecoder(JSONDecoder):
64
+ def __init__(self, *args, **kwargs) -> None:
65
+ super().__init__(object_hook=self.object_hook, *args, **kwargs)
66
+
67
+ def object_hook(self, data: dict[str, Any]) -> Any:
68
+ result = data
69
+ if "name" in data and data["name"] in ACTIONS:
70
+ result = ACTIONS[data["name"]].from_dict(data)
71
+ if isinstance(result, dict):
72
+ result = {int(k): v for k, v in result.items()}
73
+ return result
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from typing import Any
6
+
7
+ from usagi_picker.maya_utils.sets import get_sets_members
8
+
9
+
10
+ class AbstractAction(object):
11
+ NAME = "Abstract"
12
+ ARGS_TYPES = ()
13
+
14
+ @classmethod
15
+ def from_dict(cls, data: dict[str, Any]) -> AbstractAction:
16
+ return cls(args=data["args"], mandatory=data["mandatory"])
17
+
18
+ def __init__(self, args: tuple[Any, ...], mandatory: bool = False) -> None:
19
+ self.args = args
20
+ self.mandatory = mandatory
21
+
22
+ def __call__(self, namespace: str) -> None:
23
+ raise NotImplementedError
24
+
25
+ def serialize(self) -> dict[str, Any]:
26
+ return {
27
+ "name": self.NAME,
28
+ "args": self.args,
29
+ "mandatory": self.mandatory,
30
+ }
31
+
32
+ @classmethod
33
+ def deserialize(cls, data: dict[str, Any]) -> AbstractAction:
34
+ return cls.from_dict(data)
35
+
36
+ def validate(self, namespace: str) -> bool:
37
+ return True
38
+
39
+
40
+ class AbstractActionOnNodes(AbstractAction):
41
+ ARGS_TYPES = (("Nodes", "node_list"),)
42
+
43
+ def __init__(self, *args, **kwargs) -> None:
44
+ super().__init__(*args, **kwargs)
45
+ self.nodes = self.args[0]
46
+
47
+ def get_nodes(self, namespace: str) -> list[str]:
48
+ nodes = list()
49
+ if self.args and self.nodes:
50
+ nodes = cmds.ls([f"{namespace}:{node}" for node in self.nodes])
51
+ if not nodes:
52
+ cmds.warning(
53
+ f"No objects found for {self.nodes} in namespace {namespace}"
54
+ )
55
+ return
56
+ else:
57
+ nodes = cmds.ls(selection=True, type="transform")
58
+ if not nodes:
59
+ cmds.warning("Nothing is selected, please select some controllers")
60
+ return
61
+
62
+ result = list()
63
+ for node in nodes:
64
+ if cmds.nodeType(node) == "objectSet":
65
+ result.extend(get_sets_members(node))
66
+ else:
67
+ result.append(node)
68
+ return result
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from usagi_picker.qsagi.qt import QComboBox, QMenu
4
+ from usagi_picker.qsagi.qt import QCursor
5
+ from usagi_picker.qsagi.qt import qt_exec
6
+
7
+ from functools import partial
8
+
9
+ from usagi_picker.core.actions.abstract import AbstractAction
10
+ from usagi_picker.qsagi.maya_utils import maya_main_window
11
+
12
+
13
+ def _combobox_default_func(combo: QComboBox) -> None:
14
+ from usagi_picker.core.actions import ACTIONS
15
+
16
+ actions = ["Separator"]
17
+ for name, action_type in sorted(ACTIONS.items(), key=lambda x: x[0]):
18
+ if len(action_type.ARGS_TYPES) == 1 and action_type.ARGS_TYPES[0][0] == "Nodes":
19
+ actions.append(name)
20
+ combo.addItems(actions)
21
+
22
+
23
+ class ContextMenuAction(AbstractAction):
24
+ NAME = "Context Menu"
25
+ ARGS_TYPES = (
26
+ ("Nodes", "node_list"),
27
+ (
28
+ "Menu Actions",
29
+ "list",
30
+ QComboBox,
31
+ "currentText",
32
+ "setCurrentText",
33
+ "currentIndexChanged",
34
+ _combobox_default_func,
35
+ ),
36
+ )
37
+
38
+ def __init__(self, *args, **kwargs) -> None:
39
+ super().__init__(*args, **kwargs)
40
+ self.nodes, self.menu_actions = self.args
41
+
42
+ def __call__(self, namespace: str) -> None:
43
+ from usagi_picker.core.actions import ACTIONS
44
+
45
+ menu = QMenu(parent=maya_main_window())
46
+ for action_name in self.menu_actions:
47
+ if action_name == "Separator":
48
+ menu.addSeparator()
49
+ else:
50
+ action = ACTIONS[action_name]([self.nodes])
51
+ if action.validate(namespace):
52
+ menu.addAction(action_name).triggered.connect(
53
+ partial(action, namespace)
54
+ )
55
+ qt_exec(menu, QCursor.pos())
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractAction
6
+
7
+
8
+ class DeselectAction(AbstractAction):
9
+ NAME = "Deselect"
10
+ ARGS_TYPES = (("Nodes", "node_list"),)
11
+
12
+ def __call__(self, namespace: str) -> None:
13
+ if not self.args:
14
+ return
15
+ nodes = self.args[0]
16
+ if not nodes:
17
+ return
18
+ to_select = cmds.ls([f"{namespace}:{node}" for node in nodes])
19
+ if to_select:
20
+ cmds.select(to_select, deselect=True)
21
+ else:
22
+ cmds.warning(f"No objects found for {nodes} in namespace {namespace}")
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class KeyAll(AbstractActionOnNodes):
9
+ NAME = "Key All"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+ cmds.setKeyframe(nodes)
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class KeyCustoms(AbstractActionOnNodes):
9
+ NAME = "Key Customs"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+ for node in nodes:
16
+ custom_attributes = (
17
+ cmds.listAttr(node, userDefined=True, keyable=True) or []
18
+ )
19
+ if custom_attributes:
20
+ cmds.setKeyframe(nodes, attribute=custom_attributes)
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class KeyRotate(AbstractActionOnNodes):
9
+ NAME = "Key Rotate"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+ cmds.setKeyframe(nodes, attribute=("rx", "ry", "rz"))
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class KeyScale(AbstractActionOnNodes):
9
+ NAME = "Key Scale"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+ cmds.setKeyframe(nodes, attribute=("sx", "sy", "sz"))
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from maya import cmds
4
+
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class KeyTranslate(AbstractActionOnNodes):
9
+ NAME = "Key Translate"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+ cmds.setKeyframe(nodes, attribute=("tx", "ty", "tz"))
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from usagi_picker.maya_utils.ik_fk import toggle_from_nodes
5
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
6
+
7
+
8
+ class MatchIKFKAction(AbstractActionOnNodes):
9
+ NAME = "Match IK/FK"
10
+
11
+ def __call__(self, namespace: str) -> None:
12
+ nodes = self.get_nodes(namespace)
13
+ if not nodes:
14
+ return
15
+
16
+ toggle_from_nodes(nodes)
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from usagi_picker.core.actions.abstract import AbstractActionOnNodes
5
+ from usagi_picker.maya_utils.mirror import has_mirror_value, mirror_transforms
6
+
7
+
8
+ class Mirror(AbstractActionOnNodes):
9
+ NAME = "Mirror"
10
+
11
+ def validate(self, namespace: str) -> bool:
12
+ return all(has_mirror_value(f"{namespace}:{node}") for node in self.nodes)
13
+
14
+ def __call__(self, namespace: str) -> None:
15
+ nodes = self.get_nodes(namespace)
16
+ if not nodes:
17
+ return
18
+ for node in nodes:
19
+ mirror_transforms(node)