├── .gitattributes ├── .gitignore ├── DOCS.md ├── LICENSE.md ├── README.md ├── config └── readme.md ├── install.gif ├── install.mel ├── install.py ├── install.txt └── src ├── mutils ├── README.md ├── __init__.py ├── animation.py ├── attribute.py ├── cmds.py ├── decorators.py ├── gui │ ├── __init__.py │ ├── framerangemenu.py │ ├── modelpanelwidget.py │ ├── thumbnailcapturedialog.py │ └── thumbnailcapturemenu.py ├── matchnames.py ├── mirrortable.py ├── namespace.py ├── node.py ├── playblast.py ├── pose.py ├── scriptjob.py ├── selectionset.py ├── tests │ ├── __init__.py │ ├── data │ │ ├── anim.ma │ │ ├── animation.anim │ │ ├── pose.json │ │ ├── sphere.ma │ │ ├── test.ma │ │ ├── test_anim.ma │ │ ├── test_bake_connected.anim │ │ │ ├── animation.ma │ │ │ ├── animation.mb │ │ │ └── pose.json │ │ ├── test_bake_connected.ma │ │ ├── test_load_insert.anim │ │ │ ├── animation.ma │ │ │ ├── animation.mb │ │ │ └── pose.json │ │ ├── test_load_replace.anim │ │ │ ├── animation.ma │ │ │ ├── animation.mb │ │ │ └── pose.json │ │ ├── test_load_replace_completely.anim │ │ │ ├── animation.ma │ │ │ ├── animation.mb │ │ │ └── pose.json │ │ ├── test_mirror_tables.ma │ │ ├── test_non_unique_names.ma │ │ ├── test_non_unique_names.pose │ │ ├── test_older_version.anim │ │ │ ├── .studioLibrary │ │ │ │ └── record.dict │ │ │ ├── animation.ma │ │ │ └── pose.dict │ │ ├── test_older_version.dict │ │ ├── test_pose.ma │ │ ├── test_pose.pose │ │ └── test_poseplugin.pose │ │ │ └── .studiolibrary │ │ │ └── record.dict │ ├── run.py │ ├── test_anim.py │ ├── test_attribute.py │ ├── test_base.py │ ├── test_match.py │ ├── test_mirrortable.py │ ├── test_pose.py │ └── test_utils.py └── transferobject.py ├── studiolibrary ├── __init__.py ├── config.py ├── config │ └── default.json ├── folderitem.py ├── library.py ├── libraryitem.py ├── librarywindow.py ├── main.py ├── resource.py ├── resource │ ├── css │ │ └── default.css │ ├── fonts │ │ ├── LICENSE.txt │ │ ├── OpenSans-Bold.ttf │ │ ├── OpenSans-BoldItalic.ttf │ │ ├── OpenSans-ExtraBold.ttf │ │ ├── OpenSans-ExtraBoldItalic.ttf │ │ ├── OpenSans-Italic.ttf │ │ ├── OpenSans-Light.ttf │ │ ├── OpenSans-LightItalic.ttf │ │ ├── OpenSans-Regular.ttf │ │ ├── OpenSans-SemiBold.ttf │ │ └── OpenSans-SemiBoldItalic.ttf │ └── icons │ │ ├── add.png │ │ ├── add_28.png │ │ ├── archive.svg │ │ ├── arrows-rotate.svg │ │ ├── asset.svg │ │ ├── assets.svg │ │ ├── bars.svg │ │ ├── blank.png │ │ ├── book.svg │ │ ├── branch_closed_black.png │ │ ├── branch_closed_white.png │ │ ├── branch_open_black.png │ │ ├── branch_open_white.png │ │ ├── camera-alt.svg │ │ ├── camera.svg │ │ ├── cancel.png │ │ ├── caret-down.svg │ │ ├── caret-right.svg │ │ ├── character.svg │ │ ├── check_box_checked_black.png │ │ ├── check_box_checked_white.png │ │ ├── check_box_unchecked_black.png │ │ ├── check_box_unchecked_white.png │ │ ├── circle.png │ │ ├── circle.svg │ │ ├── cloud.svg │ │ ├── cog.png │ │ ├── columns-3.svg │ │ ├── critical.png │ │ ├── cross.png │ │ ├── database.svg │ │ ├── delete.png │ │ ├── drop_arrow_black.png │ │ ├── drop_arrow_white.png │ │ ├── environment.svg │ │ ├── error.png │ │ ├── expand.svg │ │ ├── eye-slash.svg │ │ ├── eye.svg │ │ ├── face.svg │ │ ├── favorite.svg │ │ ├── file-alt.svg │ │ ├── file.svg │ │ ├── filter.svg │ │ ├── folder.svg │ │ ├── folder_collapsed.png │ │ ├── folder_expanded.png │ │ ├── folder_item.png │ │ ├── folder_open.png │ │ ├── folder_open.svg │ │ ├── globe.svg │ │ ├── groupby.png │ │ ├── hand.svg │ │ ├── header.png │ │ ├── icon.png │ │ ├── image.png │ │ ├── inbox.svg │ │ ├── info.png │ │ ├── layers.svg │ │ ├── lock.svg │ │ ├── logo.png │ │ ├── logo_high.png │ │ ├── logo_white.png │ │ ├── magnifying-glass.svg │ │ ├── plus-large.svg │ │ ├── plus.svg │ │ ├── preview_placeholder.png │ │ ├── question.png │ │ ├── radio_button_checked_black.png │ │ ├── radio_button_checked_white.png │ │ ├── radio_button_unchecked_black.png │ │ ├── radio_button_unchecked_white.png │ │ ├── search.png │ │ ├── search.svg │ │ ├── settings.png │ │ ├── share.svg │ │ ├── shot.svg │ │ ├── sliders.svg │ │ ├── sortby.png │ │ ├── sync.png │ │ ├── tag.png │ │ ├── thumbnail.png │ │ ├── thumbnail_solid.png │ │ ├── times.svg │ │ ├── trash.svg │ │ ├── tree.svg │ │ ├── user.svg │ │ ├── users.svg │ │ ├── vehicle.svg │ │ ├── video.svg │ │ ├── view_all.png │ │ ├── view_compact.png │ │ ├── view_settings.png │ │ ├── warning.png │ │ └── xmark.svg ├── utils.py └── widgets │ ├── PlaceholderWidget.ui │ ├── PreviewWidget.ui │ ├── __init__.py │ ├── colorpicker.py │ ├── fieldwidgets.py │ ├── filterbymenu.py │ ├── formwidget.py │ ├── groupboxwidget.py │ ├── groupbymenu.py │ ├── iconpicker.py │ ├── itemswidget │ ├── __init__.py │ ├── groupitem.py │ ├── item.py │ ├── itemdelegate.py │ ├── itemswidget.py │ ├── itemviewmixin.py │ ├── listview.py │ └── treewidget.py │ ├── librariesmenu.py │ ├── lightbox.py │ ├── lineedit.py │ ├── menubarwidget.py │ ├── messagebox.py │ ├── placeholderwidget.py │ ├── previewwidget.py │ ├── searchwidget.py │ ├── separatoraction.py │ ├── sequencewidget.py │ ├── settings.py │ ├── sidebarwidget │ ├── __init__.py │ ├── sidebarwidget.py │ └── sidebarwidgetitem.py │ ├── slideraction.py │ ├── sortbymenu.py │ ├── statuswidget.py │ ├── themesmenu.py │ └── toastwidget.py ├── studiolibrarymaya ├── BaseLoadWidget.ui ├── BaseSaveWidget.ui ├── README.md ├── __init__.py ├── animitem.py ├── baseitem.py ├── baseloadwidget.py ├── basesavewidget.py ├── exampleitem.py ├── icons │ ├── animation.png │ ├── arrow.png │ ├── file.png │ ├── insert.png │ ├── insertConnect.png │ ├── merge.png │ ├── mergeConnect.png │ ├── mirrortable.png │ ├── pose.png │ ├── replace.png │ ├── replaceCompletely.png │ ├── replaceConnect.png │ ├── selectionSet.png │ └── selectionSet2.png ├── mayafileitem.py ├── mayalibrarywindow.py ├── mirroritem.py ├── poseitem.py ├── setsitem.py └── setsmenu.py ├── studioqt ├── __init__.py ├── color.py ├── decorators.py ├── icon.py ├── imagesequence.py ├── menu.py ├── pixmap.py ├── stylesheet.py └── utils.py └── studiovendor ├── Qt.py ├── __init__.py └── six.py /.gitattributes: -------------------------------------------------------------------------------- 1 | packages/mutils/tests/data/* linguist-vendored -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | config/config.json 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | .idea/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask instance folder 59 | instance/ 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # ========================= 93 | # Operating System Files 94 | # ========================= 95 | 96 | # OSX 97 | # ========================= 98 | 99 | .DS_Store 100 | .AppleDouble 101 | .LSOverride 102 | 103 | # Thumbnails 104 | ._* 105 | 106 | # Files that might appear in the root of a volume 107 | .DocumentRevisions-V100 108 | .fseventsd 109 | .Spotlight-V100 110 | .TemporaryItems 111 | .Trashes 112 | .VolumeIcon.icns 113 | 114 | # Directories potentially created on remote AFP share 115 | .AppleDB 116 | .AppleDesktop 117 | Network Trash Folder 118 | Temporary Items 119 | .apdisk 120 | 121 | # Windows 122 | # ========================= 123 | 124 | # Windows image file caches 125 | Thumbs.db 126 | ehthumbs.db 127 | 128 | # Folder config file 129 | Desktop.ini 130 | 131 | # Recycle Bin used on file shares 132 | $RECYCLE.BIN/ 133 | 134 | # Windows Installer files 135 | *.cab 136 | *.msi 137 | *.msm 138 | *.msp 139 | 140 | # Windows shortcuts 141 | *.lnk 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Studio Library is a python-based Qt tool for managing poses and animation in Maya. 6 | 7 | ## Features 8 | 9 | * Save poses and animation 10 | * Mirror poses and animation 11 | * Create easy to use selection sets 12 | * MMB drag for fast pose blending 13 | * LMB drag and drop to organize items 14 | * Insert, merge and replace animation 15 | * Supports Windows, Linux and OSX 16 | * Supports Maya 2018+ 17 | 18 | ## Tutorials 19 | 20 | * [How to use poses](https://www.youtube.com/watch?v=lpaWrT7VXfM) 21 | * [How to use selection sets](https://www.youtube.com/watch?v=xejWubal_j8) 22 | * [How to use mirror tables](https://www.youtube.com/watch?v=kCv0XleJfjU&t=3s) 23 | 24 | ## Installation 25 | 26 | 1. Download and unzip the *studiolibrary.zip* file from [github releases](https://github.com/krathjen/studiolibrary/releases) or [website](http://www.studiolibrary.com/download). 27 | 28 | 2. Drag and drop the "studiolibrary/install.mel" file onto the Maya viewport. 29 | 30 | 3. Click the Studio Library icon on the shelf to run. 31 | 32 | Tip: If you would like to share the Studio Library with other users then 33 | place the unzipped "studiolibrary" folder on a network drive. 34 | 35 | 36 | 37 | 38 | 39 | ## Documentation 40 | 41 | Find the latest documentation [here](DOCS.md). 42 | 43 | 44 | ## Contributing 45 | 46 | Contributions to Studio Library are always welcome! Whether it's reporting bugs, feature requests, discussing ideas or committing code. 47 | 48 | We follow the below guides for... 49 | 50 | * [Commit messages style](https://github.com/erlang/otp/wiki/Writing-good-commit-messages) 51 | * [GitHub Forking Workflow](https://gist.github.com/Chaser324/ce0505fbed06b947d962) 52 | * [Python Qt Style Guidelines](http://bitesofcode.blogspot.co.uk/2011/10/pyqt-coding-style-guidelines.html) 53 | 54 | ## License 55 | 56 | The Studio Library is free to use in production under the GNU Lesser General Public License v3.0. 57 | For more information please click [here](LICENSE.md). 58 | 59 | 60 | ## Support 61 | 62 | Comments, suggestions and bug reports are welcome. 63 | 64 | Feel free to submit any issues with the error message and a detailed step by step process of how you got the error in [github issues](https://github.com/krathjen/studiolibrary/issues/new) or contact [support@studiolibrary.com](support@studiolibrary.com). -------------------------------------------------------------------------------- /config/readme.md: -------------------------------------------------------------------------------- 1 | 2 | ## Config 3 | 4 | There are two ways to create a custom config. 5 | 6 | 1. You can create a config.json file in this directory. 7 | The config.json file will override any keys in the [default.json](../src/studiolibrary/config/default.json) file 8 | and will be ignored by git. 9 | 10 | 2. The other way is to create an environment variable with the name 11 | STUDIO_LIBRARY_CONFIG_PATH. The value of this variable should be the 12 | full path to your config.json file. 13 | 14 | The config uses the json file type with basic support for comments using "//". 15 | 16 | ##### Example: 17 | 18 | ```javascript 19 | // config.json 20 | { 21 | // The maximum walking depth from the root directory 22 | "recursiveSearchDepth": 4 23 | } 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/install.gif -------------------------------------------------------------------------------- /install.mel: -------------------------------------------------------------------------------- 1 | /* 2 | Drag and drop for Maya 2016+ 3 | */ 4 | 5 | global proc studioLibraryInstall() { 6 | string $whatIs = `whatIs studioLibraryInstall`; 7 | string $path = `substring $whatIs 25 999`; 8 | string $filename = `substitute "install.mel" $path "install.py"`;; 9 | python("import imp;imp.load_source('_studioLibraryInstall', '"+$filename+"')"); 10 | } 11 | 12 | studioLibraryInstall(); -------------------------------------------------------------------------------- /install.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation, either 6 | # version 3 of the License, or (at your option) any later version. 7 | # This library is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # Lesser General Public License for more details. 11 | # You should have received a copy of the GNU Lesser General Public 12 | # License along with this library. If not, see . 13 | """ 14 | Drag and drop for Maya 2018+ 15 | """ 16 | import os 17 | import sys 18 | 19 | 20 | try: 21 | import maya.mel 22 | import maya.cmds 23 | isMaya = True 24 | except ImportError: 25 | isMaya = False 26 | 27 | 28 | def onMayaDroppedPythonFile(*args, **kwargs): 29 | """This function is only supported since Maya 2017 Update 3""" 30 | pass 31 | 32 | 33 | def _onMayaDropped(): 34 | """Dragging and dropping this file into the scene executes the file.""" 35 | 36 | srcPath = os.path.join(os.path.dirname(__file__), 'src') 37 | iconPath = os.path.join(srcPath, 'studiolibrary', 'resource', 'icons', 'icon.png') 38 | 39 | srcPath = os.path.normpath(srcPath) 40 | iconPath = os.path.normpath(iconPath) 41 | 42 | if not os.path.exists(iconPath): 43 | raise IOError('Cannot find ' + iconPath) 44 | 45 | for path in sys.path: 46 | if os.path.exists(path + '/studiolibrary/__init__.py'): 47 | maya.cmds.warning('Studio Library is already installed at ' + path) 48 | 49 | command = ''' 50 | # ----------------------------------- 51 | # Studio Library 52 | # www.studiolibrary.com 53 | # ----------------------------------- 54 | 55 | import os 56 | import sys 57 | 58 | if not os.path.exists(r'{path}'): 59 | raise IOError(r'The source path "{path}" does not exist!') 60 | 61 | if r'{path}' not in sys.path: 62 | sys.path.insert(0, r'{path}') 63 | 64 | import studiolibrary 65 | studiolibrary.main() 66 | '''.format(path=srcPath) 67 | 68 | shelf = maya.mel.eval('$gShelfTopLevel=$gShelfTopLevel') 69 | parent = maya.cmds.tabLayout(shelf, query=True, selectTab=True) 70 | maya.cmds.shelfButton( 71 | command=command, 72 | annotation='Studio Library', 73 | sourceType='Python', 74 | image=iconPath, 75 | image1=iconPath, 76 | parent=parent 77 | ) 78 | 79 | # print("\n// Studio Library has been added to current shelf.") 80 | 81 | 82 | if isMaya: 83 | _onMayaDropped() 84 | -------------------------------------------------------------------------------- /install.txt: -------------------------------------------------------------------------------- 1 | How to install the Studio Library. 2 | 3 | 1. Drag and drop the "install.mel" file onto the Maya viewport. 4 | 5 | 2. Click the Studio Library icon on the shelf to run. 6 | 7 | Tip: If you would like to share the Studio Library with other users then 8 | place the downloaded "studiolibrary" folder on a network drive. 9 | 10 | Support: 11 | 12 | www.studiolibrary.com -------------------------------------------------------------------------------- /src/mutils/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to mutils! 2 | 3 | `mutils` is a python package containing many Maya utility functions for managing poses and animations in Maya. 4 | 5 | * www.studiolibrary.com 6 | -------------------------------------------------------------------------------- /src/mutils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from .cmds import * 14 | from .decorators import * 15 | 16 | from . import playblast 17 | from . import namespace 18 | 19 | from .scriptjob import ScriptJob 20 | from .matchnames import matchNames, groupObjects 21 | 22 | from .node import Node 23 | from .attribute import Attribute 24 | 25 | from .transferobject import TransferObject 26 | 27 | from .selectionset import SelectionSet, saveSelectionSet 28 | from .pose import Pose, savePose, loadPose 29 | from .animation import Animation, PasteOption, saveAnim, loadAnims 30 | from .mirrortable import MirrorTable, MirrorOption, saveMirrorTable 31 | -------------------------------------------------------------------------------- /src/mutils/decorators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import time 14 | import logging 15 | 16 | try: 17 | import maya.cmds 18 | except ImportError: 19 | import traceback 20 | traceback.print_exc() 21 | 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | __all__ = [ 27 | "timing", 28 | "unifyUndo", 29 | "disableUndo", 30 | "disableViews", 31 | "disableAutoKey", 32 | "showWaitCursor", 33 | "restoreSelection", 34 | "restoreCurrentTime", 35 | ] 36 | 37 | 38 | def timing(fn): 39 | 40 | def wrapped(*args, **kwargs): 41 | time1 = time.time() 42 | ret = fn(*args, **kwargs) 43 | time2 = time.time() 44 | logger.debug('%s function took %0.5f sec' % (fn.__name__, (time2 - time1))) 45 | return ret 46 | 47 | return wrapped 48 | 49 | 50 | def unifyUndo(fn): 51 | 52 | def wrapped(*args, **kwargs): 53 | maya.cmds.undoInfo(openChunk=True) 54 | try: 55 | return fn(*args, **kwargs) 56 | finally: 57 | maya.cmds.undoInfo(closeChunk=True) 58 | 59 | wrapped.__name__ = fn.__name__ 60 | wrapped.__doc__ = fn.__doc__ 61 | 62 | return wrapped 63 | 64 | 65 | def disableUndo(fn): 66 | 67 | def wrapped(*args, **kwargs): 68 | initialUndoState = maya.cmds.undoInfo(q=True, state=True) 69 | maya.cmds.undoInfo(stateWithoutFlush=False) 70 | try: 71 | return fn(*args, **kwargs) 72 | finally: 73 | maya.cmds.undoInfo(stateWithoutFlush=initialUndoState) 74 | 75 | wrapped.__name__ = fn.__name__ 76 | wrapped.__doc__ = fn.__doc__ 77 | 78 | return wrapped 79 | 80 | 81 | def disableAutoKey(fn): 82 | 83 | def wrapped(*args, **kwargs): 84 | initialState = maya.cmds.autoKeyframe(query=True, state=True) 85 | maya.cmds.autoKeyframe(edit=True, state=False) 86 | try: 87 | return fn(*args, **kwargs) 88 | finally: 89 | maya.cmds.autoKeyframe(edit=True, state=initialState) 90 | 91 | wrapped.__name__ = fn.__name__ 92 | wrapped.__doc__ = fn.__doc__ 93 | 94 | return wrapped 95 | 96 | 97 | def restoreSelection(fn): 98 | 99 | def wrapped(*args, **kwargs): 100 | selection = maya.cmds.ls(selection=True) or [] 101 | try: 102 | return fn(*args, **kwargs) 103 | finally: 104 | if selection: 105 | maya.cmds.select(selection) 106 | 107 | wrapped.__name__ = fn.__name__ 108 | wrapped.__doc__ = fn.__doc__ 109 | 110 | return wrapped 111 | 112 | 113 | def restoreCurrentTime(fn): 114 | 115 | def wrapped(*args, **kwargs): 116 | initialTime = maya.cmds.currentTime(query=True) 117 | try: 118 | return fn(*args, **kwargs) 119 | finally: 120 | maya.cmds.currentTime(initialTime, edit=True) 121 | 122 | wrapped.__name__ = fn.__name__ 123 | wrapped.__doc__ = fn.__doc__ 124 | 125 | return wrapped 126 | 127 | 128 | def showWaitCursor(fn): 129 | 130 | def wrapped(*args, **kwargs): 131 | maya.cmds.waitCursor(state=True) 132 | try: 133 | return fn(*args, **kwargs) 134 | finally: 135 | maya.cmds.waitCursor(state=False) 136 | 137 | wrapped.__name__ = fn.__name__ 138 | wrapped.__doc__ = fn.__doc__ 139 | 140 | return wrapped 141 | 142 | 143 | def disableViews(fn): 144 | 145 | def wrapped(*args, **kwargs): 146 | modelPanels = maya.cmds.getPanel(vis=True) 147 | emptySelConn = maya.cmds.selectionConnection() 148 | 149 | for panel in modelPanels: 150 | if maya.cmds.getPanel(to=panel) == 'modelPanel': 151 | maya.cmds.isolateSelect(panel, state=True) 152 | maya.cmds.modelEditor(panel, e=True, mlc=emptySelConn) 153 | 154 | try: 155 | return fn(*args, **kwargs) 156 | finally: 157 | for panel in modelPanels: 158 | if maya.cmds.getPanel(to=panel) == 'modelPanel': 159 | maya.cmds.isolateSelect(panel, state=False) 160 | 161 | maya.cmds.deleteUI(emptySelConn) 162 | 163 | wrapped.__name__ = fn.__name__ 164 | wrapped.__doc__ = fn.__doc__ 165 | 166 | return wrapped 167 | -------------------------------------------------------------------------------- /src/mutils/gui/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtCore 14 | from studiovendor.Qt import QtCompat 15 | from studiovendor.Qt import QtWidgets 16 | 17 | try: 18 | import maya.OpenMayaUI as omui 19 | except ImportError as error: 20 | print(error) 21 | 22 | from .framerangemenu import FrameRangeMenu 23 | from .framerangemenu import showFrameRangeMenu 24 | from .modelpanelwidget import ModelPanelWidget 25 | from .thumbnailcapturedialog import * 26 | from .thumbnailcapturemenu import ThumbnailCaptureMenu 27 | 28 | 29 | def mayaWindow(): 30 | """ 31 | Return the Maya main window as a QMainWindow object. 32 | 33 | :rtype: QMainWindow 34 | """ 35 | mainWindowPtr = omui.MQtUtil.mainWindow() 36 | return QtCompat.wrapInstance(mainWindowPtr, QtWidgets.QMainWindow) 37 | 38 | 39 | def makeMayaStandaloneWindow(w): 40 | """ 41 | Make a standalone window, though parented under Maya's mainWindow. 42 | 43 | The parenting under Maya's mainWindow is done so that the QWidget will 44 | not auto-destroy itself when the instance variable goes out of scope. 45 | """ 46 | # Parent under the main Maya window 47 | w.setParent(mayaWindow()) 48 | 49 | # Make this widget appear as a standalone window 50 | w.setWindowFlags(QtCore.Qt.Window) 51 | w.raise_() 52 | w.show() 53 | -------------------------------------------------------------------------------- /src/mutils/gui/framerangemenu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtGui 14 | from studiovendor.Qt import QtWidgets 15 | 16 | import mutils 17 | 18 | 19 | __all__ = ["FrameRangeMenu", "showFrameRangeMenu"] 20 | 21 | 22 | class FrameRangeAction(QtWidgets.QAction): 23 | def __init__(self, *args): 24 | super(FrameRangeAction, self).__init__(*args) 25 | 26 | self._frameRange = (0, 100) 27 | 28 | def frameRange(self): 29 | return self._frameRange 30 | 31 | def setFrameRange(self, frameRange): 32 | self._frameRange = frameRange 33 | 34 | 35 | class FrameRangeMenu(QtWidgets.QMenu): 36 | 37 | def __init__(self, parent=None): 38 | super(FrameRangeMenu, self).__init__(parent) 39 | 40 | action = FrameRangeAction("From Timeline", self) 41 | action.setFrameRange(mutils.playbackFrameRange()) 42 | self.addAction(action) 43 | 44 | action = FrameRangeAction("From Selected Timeline", self) 45 | action.setFrameRange(mutils.selectedFrameRange()) 46 | self.addAction(action) 47 | 48 | action = FrameRangeAction("From Selected Objects", self) 49 | action.setFrameRange(mutils.selectedObjectsFrameRange()) 50 | self.addAction(action) 51 | 52 | 53 | def showFrameRangeMenu(): 54 | """ 55 | Show the frame range menu at the current cursor position. 56 | 57 | :rtype: QtWidgets.QAction 58 | """ 59 | menu = FrameRangeMenu() 60 | position = QtGui.QCursor().pos() 61 | action = menu.exec_(position) 62 | return action 63 | 64 | 65 | if __name__ == "__main__": 66 | action = showFrameRangeMenu() 67 | print(action.frameRange()) 68 | -------------------------------------------------------------------------------- /src/mutils/gui/modelpanelwidget.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import mutils 14 | import mutils.gui 15 | 16 | from studiovendor.Qt import QtCore 17 | from studiovendor.Qt import QtWidgets 18 | 19 | try: 20 | import maya.cmds 21 | import maya.OpenMayaUI as mui 22 | isMaya = True 23 | except ImportError: 24 | isMaya = False 25 | 26 | 27 | __all__ = ["ModelPanelWidget"] 28 | 29 | 30 | class ModelPanelWidget(QtWidgets.QWidget): 31 | 32 | def __init__(self, parent, name="capturedModelPanel", **kwargs): 33 | super(ModelPanelWidget, self).__init__(parent, **kwargs) 34 | 35 | uniqueName = name + str(id(self)) 36 | self.setObjectName(uniqueName + 'Widget') 37 | 38 | layout = QtWidgets.QVBoxLayout() 39 | layout.setContentsMargins(0, 0, 0, 0) 40 | layout.setObjectName(uniqueName + "Layout") 41 | self.setLayout(layout) 42 | 43 | maya.cmds.setParent(layout.objectName()) 44 | self._modelPanel = maya.cmds.modelPanel(uniqueName, label="ModelPanel") 45 | self.setModelPanelOptions() 46 | 47 | def setModelPanelOptions(self): 48 | 49 | modelPanel = self.name() 50 | 51 | maya.cmds.modelEditor(modelPanel, edit=True, allObjects=False) 52 | maya.cmds.modelEditor(modelPanel, edit=True, grid=False) 53 | maya.cmds.modelEditor(modelPanel, edit=True, dynamics=False) 54 | maya.cmds.modelEditor(modelPanel, edit=True, activeOnly=False) 55 | maya.cmds.modelEditor(modelPanel, edit=True, manipulators=False) 56 | maya.cmds.modelEditor(modelPanel, edit=True, headsUpDisplay=False) 57 | maya.cmds.modelEditor(modelPanel, edit=True, selectionHiliteDisplay=False) 58 | 59 | maya.cmds.modelEditor(modelPanel, edit=True, polymeshes=True) 60 | maya.cmds.modelEditor(modelPanel, edit=True, nurbsSurfaces=True) 61 | maya.cmds.modelEditor(modelPanel, edit=True, subdivSurfaces=True) 62 | maya.cmds.modelEditor(modelPanel, edit=True, displayTextures=True) 63 | maya.cmds.modelEditor(modelPanel, edit=True, displayAppearance="smoothShaded") 64 | 65 | currentModelPanel = mutils.currentModelPanel() 66 | 67 | if currentModelPanel: 68 | camera = maya.cmds.modelEditor(currentModelPanel, query=True, camera=True) 69 | displayLights = maya.cmds.modelEditor(currentModelPanel, query=True, displayLights=True) 70 | displayTextures = maya.cmds.modelEditor(currentModelPanel, query=True, displayTextures=True) 71 | 72 | maya.cmds.modelEditor(modelPanel, edit=True, camera=camera) 73 | maya.cmds.modelEditor(modelPanel, edit=True, displayLights=displayLights) 74 | maya.cmds.modelEditor(modelPanel, edit=True, displayTextures=displayTextures) 75 | 76 | def name(self): 77 | return self._modelPanel 78 | 79 | def modelPanel(self): 80 | ptr = mui.MQtUtil.findControl(self._modelPanel) 81 | return mutils.gui.wrapInstance(ptr, QtWidgets.QWidget) 82 | 83 | def barLayout(self): 84 | name = maya.cmds.modelPanel(self._modelPanel, query=True, barLayout=True) 85 | ptr = mui.MQtUtil.findControl(name) 86 | return mutils.gui.wrapInstance(ptr, QtCore.QObject) 87 | 88 | def hideBarLayout(self): 89 | self.barLayout().hide() 90 | 91 | def hideMenuBar(self): 92 | maya.cmds.modelPanel(self._modelPanel, edit=True, menuBarVisible=False) 93 | 94 | def setCamera(self, name): 95 | maya.cmds.modelPanel(self._modelPanel, edit=True, cam=name) 96 | 97 | 98 | if __name__ == "__main__": 99 | widget = ModelPanelWidget(None, "modelPanel") 100 | widget.show() 101 | -------------------------------------------------------------------------------- /src/mutils/namespace.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | import logging 13 | import traceback 14 | from collections import OrderedDict 15 | 16 | try: 17 | import maya.cmds 18 | except ImportError: 19 | traceback.print_exc() 20 | 21 | 22 | logger = logging.getLogger(__name__) 23 | 24 | 25 | __all__ = [ 26 | "getAll", 27 | "getFromDagPath", 28 | "getFromDagPaths", 29 | "getFromSelection", 30 | ] 31 | 32 | 33 | def getFromDagPaths(dagPaths): 34 | """ 35 | :type dagPaths: list[str] 36 | :rtype: list[str] 37 | """ 38 | namespaces = [] 39 | 40 | for dagPath in dagPaths: 41 | namespace = getFromDagPath(dagPath) 42 | namespaces.append(namespace) 43 | 44 | # Remove any duplicates while retaining the order 45 | return list(OrderedDict.fromkeys(namespaces)) 46 | 47 | 48 | def getFromDagPath(dagPath): 49 | """ 50 | :type dagPath: str 51 | :rtype: str 52 | """ 53 | shortName = dagPath.split("|")[-1] 54 | namespace = ":".join(shortName.split(":")[:-1]) 55 | return namespace 56 | 57 | 58 | def getFromSelection(): 59 | """ 60 | Get the current namespaces from the selected objects in Maya. 61 | 62 | :rtype: list[str] 63 | """ 64 | namespaces = [""] 65 | 66 | try: 67 | names = maya.cmds.ls(selection=True) 68 | namespaces = getFromDagPaths(names) or namespaces 69 | except NameError as error: 70 | # Catch any errors when running this command outside of Maya 71 | logger.exception(error) 72 | 73 | return namespaces 74 | 75 | 76 | def getAll(): 77 | """ 78 | Get all the available namespaces in the scene 79 | 80 | :rtype: list[str] 81 | """ 82 | IGNORE_NAMESPACES = ['UI', 'shared'] 83 | 84 | namespaces = maya.cmds.namespaceInfo(listOnlyNamespaces=True, recurse=True) 85 | namespaces = list(set(namespaces) - set(IGNORE_NAMESPACES)) 86 | namespaces = sorted(namespaces) 87 | 88 | return namespaces 89 | -------------------------------------------------------------------------------- /src/mutils/playblast.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | import logging 15 | 16 | import mutils 17 | 18 | try: 19 | import maya.cmds 20 | except Exception: 21 | import traceback 22 | traceback.print_exc() 23 | 24 | 25 | __all__ = [ 26 | "playblast", 27 | ] 28 | 29 | logger = logging.getLogger(__name__) 30 | 31 | 32 | # Valid Renderers: 33 | # [u'vp2Renderer', u'base_OpenGL_Renderer', 34 | # u'hwRender_OpenGL_Renderer', u'stub_Renderer'] 35 | DEFAULT_PLAYBLAST_RENDERER = None 36 | 37 | 38 | class PlayblastError(Exception): 39 | """Base class for exceptions in this module.""" 40 | pass 41 | 42 | 43 | def playblast(filename, modelPanel, startFrame, endFrame, width, height, step=1): 44 | """ 45 | Wrapper for Maya's Playblast command. 46 | 47 | :type filename: str 48 | :type modelPanel: str 49 | :type startFrame: int 50 | :type endFrame: int 51 | :type width: int 52 | :type height: int 53 | :type step: list[int] 54 | :rtype: str 55 | """ 56 | logger.info(u"Playblasting '{filename}'".format(filename=filename)) 57 | 58 | if startFrame == endFrame and os.path.exists(filename): 59 | os.remove(filename) 60 | 61 | frame = [i for i in range(startFrame, endFrame + 1, step)] 62 | 63 | modelPanel = modelPanel or mutils.currentModelPanel() 64 | if maya.cmds.modelPanel(modelPanel, query=True, exists=True): 65 | maya.cmds.setFocus(modelPanel) 66 | if DEFAULT_PLAYBLAST_RENDERER: 67 | maya.cmds.modelEditor( 68 | modelPanel, 69 | edit=True, 70 | rendererName=DEFAULT_PLAYBLAST_RENDERER 71 | ) 72 | 73 | name, compression = os.path.splitext(filename) 74 | filename = filename.replace(compression, "") 75 | compression = compression.replace(".", "") 76 | offScreen = mutils.isLinux() 77 | 78 | path = maya.cmds.playblast( 79 | format="image", 80 | viewer=False, 81 | percent=100, 82 | quality=100, 83 | frame=frame, 84 | width=width, 85 | height=height, 86 | filename=filename, 87 | endTime=endFrame, 88 | startTime=startFrame, 89 | offScreen=offScreen, 90 | forceOverwrite=True, 91 | showOrnaments=False, 92 | compression=compression, 93 | ) 94 | 95 | if not path: 96 | raise PlayblastError("Playblast was canceled") 97 | 98 | src = path.replace("####", str(int(0)).rjust(4, "0")) 99 | 100 | if startFrame == endFrame: 101 | dst = src.replace(".0000.", ".") 102 | logger.info("Renaming '%s' => '%s" % (src, dst)) 103 | os.rename(src, dst) 104 | src = dst 105 | 106 | logger.info(u"Playblasted '%s'" % src) 107 | return src 108 | -------------------------------------------------------------------------------- /src/mutils/scriptjob.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import traceback 14 | 15 | try: 16 | import maya.cmds 17 | except ImportError: 18 | traceback.print_exc() 19 | 20 | 21 | class ScriptJob(object): 22 | """ 23 | self._scriptJob = mutils.ScriptJob(e=['SelectionChanged', self.selectionChanged]) 24 | """ 25 | def __init__(self, *args, **kwargs): 26 | self.id = maya.cmds.scriptJob(*args, **kwargs) 27 | 28 | def kill(self): 29 | if self.id: 30 | maya.cmds.scriptJob(kill=self.id, force=True) 31 | self.id = None 32 | 33 | def __enter__(self): 34 | return self 35 | 36 | def __exit__(self, t, v, tb): 37 | if t is not None: 38 | self.kill() 39 | -------------------------------------------------------------------------------- /src/mutils/selectionset.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import logging 14 | 15 | import mutils 16 | 17 | try: 18 | import maya.cmds 19 | except Exception: 20 | import traceback 21 | traceback.print_exc() 22 | 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | def saveSelectionSet(path, objects, metadata=None): 28 | """ 29 | Convenience function for saving a selection set to the given disc location. 30 | 31 | :type path: str 32 | :type objects: list[str] 33 | :type metadata: dict or None 34 | :type args: list 35 | :type kwargs: dict 36 | :rtype: SelectionSet 37 | """ 38 | selectionSet = SelectionSet.fromObjects(objects) 39 | 40 | if metadata: 41 | selectionSet.updateMetadata(metadata) 42 | 43 | selectionSet.save(path) 44 | 45 | return selectionSet 46 | 47 | 48 | class SelectionSet(mutils.TransferObject): 49 | 50 | def load(self, objects=None, namespaces=None, **kwargs): 51 | """ 52 | Load/Select the transfer objects to the given objects or namespaces. 53 | 54 | :type objects: list[str] or None 55 | :type namespaces: list[str] or None 56 | :type kwargs: 57 | """ 58 | validNodes = [] 59 | dstObjects = objects 60 | srcObjects = self.objects() 61 | 62 | self.validate(namespaces=namespaces) 63 | 64 | matches = mutils.matchNames( 65 | srcObjects, 66 | dstObjects=dstObjects, 67 | dstNamespaces=namespaces 68 | ) 69 | 70 | for srcNode, dstNode in matches: 71 | # Support for wild cards eg: ['*_control']. 72 | if "*" in dstNode.name(): 73 | validNodes.append(dstNode.name()) 74 | else: 75 | # Remove the first pipe in-case the object has a parent. 76 | dstNode.stripFirstPipe() 77 | 78 | # Try to get the short name. Much faster than the long 79 | # name when selecting objects. 80 | try: 81 | dstNode = dstNode.toShortName() 82 | 83 | except mutils.NoObjectFoundError as error: 84 | logger.debug(error) 85 | continue 86 | 87 | except mutils.MoreThanOneObjectFoundError as error: 88 | logger.debug(error) 89 | 90 | validNodes.append(dstNode.name()) 91 | 92 | if validNodes: 93 | maya.cmds.select(validNodes, **kwargs) 94 | 95 | # Return the focus to the Maya window 96 | maya.cmds.setFocus("MayaWindow") 97 | else: 98 | text = "No objects match when loading data. " \ 99 | "Turn on debug mode to see more details." 100 | 101 | raise mutils.NoMatchFoundError(text) 102 | 103 | def select(self, objects=None, namespaces=None, **kwargs): 104 | """ 105 | Convenience method for any classes inheriting SelectionSet. 106 | 107 | :type objects: str 108 | :type namespaces: list[str] 109 | :rtype: None 110 | """ 111 | SelectionSet.load(self, objects=objects, namespaces=namespaces, **kwargs) 112 | -------------------------------------------------------------------------------- /src/mutils/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from mutils.tests.run import run 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/mutils/tests/data/anim.ma: -------------------------------------------------------------------------------- 1 | //Maya ASCII 2013 scene 2 | //Name: anim.ma 3 | //Last modified: Wed, Aug 20, 2014 07:33:27 PM 4 | //Codeset: UTF-8 5 | requires maya "2013"; 6 | requires "stereoCamera" "10.0"; 7 | currentUnit -l centimeter -a degree -t film; 8 | fileInfo "application" "maya"; 9 | fileInfo "product" "Maya batch mode"; 10 | fileInfo "version" "2013 Service Pack 2P12 x64"; 11 | fileInfo "cutIdentifier" "201304120319-868747"; 12 | fileInfo "osv" "Linux 3.5.4-2.10-desktop #1 SMP PREEMPT Fri Oct 5 14:56:49 CEST 2012 x86_64"; 13 | createNode animCurveTA -n "CURVE1"; 14 | setAttr ".tan" 18; 15 | setAttr ".wgt" no; 16 | setAttr -s 2 ".ktv[0:1]" 1 45 10 30.81018202226841; 17 | createNode animCurveTU -n "CURVE2"; 18 | setAttr ".tan" 9; 19 | setAttr ".wgt" no; 20 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 21 | setAttr -s 2 ".kot[0:1]" 5 5; 22 | createNode animCurveTL -n "CURVE3"; 23 | setAttr ".tan" 18; 24 | setAttr ".wgt" no; 25 | setAttr -s 2 ".ktv[0:1]" 1 0 10 0; 26 | createNode animCurveTL -n "CURVE4"; 27 | setAttr ".tan" 18; 28 | setAttr ".wgt" no; 29 | setAttr -s 2 ".ktv[0:1]" 1 8 10 8; 30 | createNode animCurveTL -n "CURVE5"; 31 | setAttr ".tan" 18; 32 | setAttr ".wgt" no; 33 | setAttr -s 2 ".ktv[0:1]" 1 -12 10 11.214436147065292; 34 | createNode animCurveTU -n "CURVE6"; 35 | setAttr ".tan" 18; 36 | setAttr ".wgt" no; 37 | setAttr -s 2 ".ktv[0:1]" 1 0.666 10 0.666; 38 | createNode animCurveTU -n "CURVE7"; 39 | setAttr ".tan" 18; 40 | setAttr ".wgt" no; 41 | setAttr -s 2 ".ktv[0:1]" 1 0 10 10; 42 | createNode animCurveTU -n "CURVE8"; 43 | setAttr ".tan" 18; 44 | setAttr ".wgt" no; 45 | setAttr -s 2 ".ktv[0:1]" 1 0.25 10 0.42958527814637792; 46 | createNode animCurveTU -n "CURVE9"; 47 | setAttr ".tan" 9; 48 | setAttr ".wgt" no; 49 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 50 | setAttr -s 2 ".kot[0:1]" 5 5; 51 | createNode animCurveTU -n "CURVE10"; 52 | setAttr ".tan" 18; 53 | setAttr ".wgt" no; 54 | setAttr -s 2 ".ktv[0:1]" 1 0.2 10 0.2; 55 | createNode animCurveTU -n "CURVE11"; 56 | setAttr ".tan" 18; 57 | setAttr ".wgt" no; 58 | setAttr -s 2 ".ktv[0:1]" 1 1.4 10 1.4; 59 | createNode animCurveTA -n "CURVE12"; 60 | setAttr ".tan" 18; 61 | setAttr ".wgt" no; 62 | setAttr -s 2 ".ktv[0:1]" 1 90 10 149.70880463068096; 63 | createNode animCurveTU -n "CURVE13"; 64 | setAttr ".tan" 18; 65 | setAttr ".wgt" no; 66 | setAttr -s 2 ".ktv[0:1]" 1 2.6 10 2.6; 67 | createNode animCurveTU -n "CURVE14"; 68 | setAttr ".tan" 18; 69 | setAttr ".wgt" no; 70 | setAttr -s 2 ".ktv[0:1]" 1 0.5 10 0.85917055629275585; 71 | createNode animCurveTU -n "CURVE15"; 72 | setAttr ".tan" 18; 73 | setAttr ".wgt" no; 74 | setAttr -s 2 ".ktv[0:1]" 1 5 10 5; 75 | createNode animCurveTU -n "CURVE16"; 76 | setAttr ".tan" 9; 77 | setAttr ".wgt" no; 78 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 79 | setAttr -s 2 ".kot[0:1]" 5 5; 80 | createNode animCurveTL -n "CURVE18"; 81 | setAttr ".tan" 18; 82 | setAttr ".wgt" no; 83 | setAttr -s 2 ".ktv[0:1]" 1 0 10 15; 84 | // End -------------------------------------------------------------------------------- /src/mutils/tests/data/animation.anim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/mutils/tests/data/animation.anim -------------------------------------------------------------------------------- /src/mutils/tests/data/pose.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "angularUnit": "deg", 4 | "description": "test shot", 5 | "user": "testuser", 6 | "startTime": 1, 7 | "mayaSceneFile": "/norman/work/krathjen/git/sandbox/site-packages/mutils/tests/data/test_anim.ma", 8 | "timeUnit": "film", 9 | "cTime": 646464451, 10 | "mayaVersion": "2013 Service Pack 2P12 x64", 11 | "linearUnit": "cm", 12 | "version": "1.0.0", 13 | "endTime": 48 14 | } 15 | , 16 | "objects": { 17 | "srcSphere:offset": { 18 | "attrs": {} 19 | }, 20 | "srcSphere:sphere": { 21 | "attrs": { 22 | "rotateX": { 23 | "curve": "CURVE1", 24 | "type": "doubleAngle", 25 | "value": 45.0 26 | }, 27 | "testEnum": { 28 | "curve": "CURVE2", 29 | "type": "enum", 30 | "value": 1 31 | }, 32 | "translateX": { 33 | "curve": "CURVE3", 34 | "type": "doubleLinear", 35 | "value": 0.0 36 | }, 37 | "translateY": { 38 | "curve": "CURVE4", 39 | "type": "doubleLinear", 40 | "value": 8.0 41 | }, 42 | "translateZ": { 43 | "curve": "CURVE5", 44 | "type": "doubleLinear", 45 | "value": -12.0 46 | }, 47 | "rotateY": { 48 | "type": "doubleAngle", 49 | "value": 50.0 50 | }, 51 | "testFloat": { 52 | "curve": "CURVE6", 53 | "type": "double", 54 | "value": 0.66600000000000004 55 | }, 56 | "testAnimated": { 57 | "curve": "CURVE7", 58 | "type": "double", 59 | "value": 0.0 60 | }, 61 | "scaleX": { 62 | "curve": "CURVE8", 63 | "type": "double", 64 | "value": 0.25 65 | }, 66 | "scaleY": { 67 | "type": "double", 68 | "value": 4.6600000000000001 69 | }, 70 | "visibility": { 71 | "curve": "CURVE9", 72 | "type": "bool", 73 | "value": true 74 | }, 75 | "testString": { 76 | "type": "string", 77 | "value": "Hello world" 78 | }, 79 | "testVectorX": { 80 | "curve": "CURVE10", 81 | "type": "double", 82 | "value": 0.20000000000000001 83 | }, 84 | "testVectorY": { 85 | "curve": "CURVE11", 86 | "type": "double", 87 | "value": 1.3999999999999999 88 | }, 89 | "rotateZ": { 90 | "curve": "CURVE12", 91 | "type": "doubleAngle", 92 | "value": 90.0 93 | }, 94 | "testVectorZ": { 95 | "curve": "CURVE13", 96 | "type": "double", 97 | "value": 2.6000000000000001 98 | }, 99 | "scaleZ": { 100 | "curve": "CURVE14", 101 | "type": "double", 102 | "value": 0.5 103 | }, 104 | "testInteger": { 105 | "curve": "CURVE15", 106 | "type": "long", 107 | "value": 5 108 | }, 109 | "testBoolean": { 110 | "curve": "CURVE16", 111 | "type": "bool", 112 | "value": true 113 | }, 114 | "testConnect": { 115 | "type": "double", 116 | "value": 8.0 117 | } 118 | } 119 | }, 120 | "srcSphere:lockedNode": { 121 | "attrs": { 122 | "translateX": { 123 | "type": "doubleLinear", 124 | "value": 0.0 125 | }, 126 | "translateY": { 127 | "type": "doubleLinear", 128 | "value": 5.0 129 | }, 130 | "translateZ": { 131 | "curve": "CURVE18", 132 | "type": "doubleLinear", 133 | "value": 0.0 134 | }, 135 | "scaleX": { 136 | "type": "double", 137 | "value": 1.0 138 | }, 139 | "scaleY": { 140 | "type": "double", 141 | "value": 1.0 142 | }, 143 | "visibility": { 144 | "type": "bool", 145 | "value": true 146 | }, 147 | "rotateX": { 148 | "type": "doubleAngle", 149 | "value": 0.0 150 | }, 151 | "rotateY": { 152 | "type": "doubleAngle", 153 | "value": 0.0 154 | }, 155 | "rotateZ": { 156 | "type": "doubleAngle", 157 | "value": 0.0 158 | }, 159 | "scaleZ": { 160 | "type": "double", 161 | "value": 1.0 162 | } 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_bake_connected.anim/animation.ma: -------------------------------------------------------------------------------- 1 | //Maya ASCII 2016 scene 2 | //Name: animation.ma 3 | //Last modified: Tue, Aug 09, 2016 08:47:07 AM 4 | //Codeset: 1252 5 | requires maya "2016"; 6 | currentUnit -l centimeter -a degree -t film; 7 | fileInfo "application" "maya"; 8 | fileInfo "product" "Maya 2016"; 9 | fileInfo "version" "2016"; 10 | fileInfo "cutIdentifier" "201502261600-953408"; 11 | fileInfo "osv" "Microsoft Windows 8 Business Edition, 64-bit (Build 9200)\n"; 12 | createNode animCurveTU -n "CURVE1"; 13 | rename -uid "B3A2D98C-465C-F630-EA7F-7A960B6B0B4D"; 14 | setAttr ".tan" 18; 15 | setAttr ".wgt" no; 16 | setAttr -s 2 ".ktv[0:1]" 1 0 10 10; 17 | createNode animCurveTU -n "CURVE2"; 18 | rename -uid "3A3C753F-43CD-18A5-3E19-E9B3F445D832"; 19 | setAttr ".tan" 18; 20 | setAttr ".wgt" no; 21 | setAttr -s 10 ".ktv[0:9]" 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9 8 10 8; 22 | setAttr -s 10 ".kit[9]" 1; 23 | setAttr -s 10 ".kot[0:9]" 1 18 18 18 18 18 18 18 24 | 18 18; 25 | setAttr -s 10 ".kix[9]" 1; 26 | setAttr -s 10 ".kiy[9]" 0; 27 | setAttr -s 10 ".kox[0:9]" 1 1 1 1 1 1 1 1 1 1; 28 | setAttr -s 10 ".koy[0:9]" 0 0 0 0 0 0 0 0 0 0; 29 | // End -------------------------------------------------------------------------------- /src/mutils/tests/data/test_bake_connected.anim/animation.mb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/mutils/tests/data/test_bake_connected.anim/animation.mb -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_insert.anim/animation.ma: -------------------------------------------------------------------------------- 1 | //Maya ASCII 2016 scene 2 | //Name: animation.ma 3 | //Last modified: Tue, Aug 09, 2016 08:47:07 AM 4 | //Codeset: 1252 5 | requires maya "2016"; 6 | currentUnit -l centimeter -a degree -t film; 7 | fileInfo "application" "maya"; 8 | fileInfo "product" "Maya 2016"; 9 | fileInfo "version" "2016"; 10 | fileInfo "cutIdentifier" "201502261600-953408"; 11 | fileInfo "osv" "Microsoft Windows 8 Business Edition, 64-bit (Build 9200)\n"; 12 | createNode animCurveTU -n "CURVE1"; 13 | rename -uid "B4FA69EE-4158-445F-5AB5-769D92B60EE3"; 14 | setAttr ".tan" 18; 15 | setAttr ".wgt" no; 16 | setAttr -s 2 ".ktv[0:1]" 1 5 10 5; 17 | createNode animCurveTU -n "CURVE2"; 18 | rename -uid "CA0863B5-4680-36D0-E9A3-83A0A33EC51B"; 19 | setAttr ".tan" 9; 20 | setAttr ".wgt" no; 21 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 22 | setAttr -s 2 ".kot[0:1]" 5 5; 23 | createNode animCurveTU -n "CURVE3"; 24 | rename -uid "436929BD-4536-FD91-F836-FA9961D739F5"; 25 | setAttr ".tan" 18; 26 | setAttr ".wgt" no; 27 | setAttr -s 2 ".ktv[0:1]" 1 0.25 10 0.42958527814637792; 28 | createNode animCurveTU -n "CURVE4"; 29 | rename -uid "9E217B7B-4585-8318-49F7-249A1ACBA95B"; 30 | setAttr ".tan" 18; 31 | setAttr ".wgt" no; 32 | setAttr -s 2 ".ktv[0:1]" 1 0.5 10 0.85917055629275585; 33 | createNode animCurveTA -n "CURVE5"; 34 | rename -uid "5649AD20-440E-7866-8CAB-B08834F07905"; 35 | setAttr ".tan" 18; 36 | setAttr ".wgt" no; 37 | setAttr -s 2 ".ktv[0:1]" 1 45 10 30.81018202226841; 38 | createNode animCurveTA -n "CURVE6"; 39 | rename -uid "19E0B15E-449C-F085-5151-10BFD6772FE1"; 40 | setAttr ".tan" 18; 41 | setAttr ".wgt" no; 42 | setAttr -s 2 ".ktv[0:1]" 1 90 10 149.70880463068096; 43 | createNode animCurveTL -n "CURVE7"; 44 | rename -uid "1FE3DC07-4819-A244-0BC3-B8A3B3EF1275"; 45 | setAttr ".tan" 18; 46 | setAttr ".wgt" no; 47 | setAttr -s 2 ".ktv[0:1]" 1 0 10 0; 48 | createNode animCurveTL -n "CURVE8"; 49 | rename -uid "00984994-4BA2-5907-9C38-D1BE8279B667"; 50 | setAttr ".tan" 18; 51 | setAttr ".wgt" no; 52 | setAttr -s 2 ".ktv[0:1]" 1 8 10 8; 53 | createNode animCurveTL -n "CURVE9"; 54 | rename -uid "40524ED9-44E0-043B-4F9E-00ABB81FF2FA"; 55 | setAttr ".tan" 18; 56 | setAttr ".wgt" no; 57 | setAttr -s 2 ".ktv[0:1]" 1 -12 10 11.214436147065292; 58 | createNode animCurveTU -n "CURVE10"; 59 | rename -uid "240A5025-4AA3-D27B-CBD9-3D98E97E7B2A"; 60 | setAttr ".tan" 9; 61 | setAttr ".wgt" no; 62 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 63 | setAttr -s 2 ".kot[0:1]" 5 5; 64 | createNode animCurveTU -n "CURVE11"; 65 | rename -uid "4541CB45-4FD7-656E-2626-0394702423B3"; 66 | setAttr ".tan" 18; 67 | setAttr ".wgt" no; 68 | setAttr -s 2 ".ktv[0:1]" 1 0.666 10 0.666; 69 | createNode animCurveTU -n "CURVE12"; 70 | rename -uid "55FC02E9-4D2D-FF69-A838-978CC1CA83B4"; 71 | setAttr ".tan" 18; 72 | setAttr ".wgt" no; 73 | setAttr -s 2 ".ktv[0:1]" 1 0 10 10; 74 | createNode animCurveTU -n "CURVE13"; 75 | rename -uid "CBF4DD9D-4839-361A-70F2-6990E98D9C30"; 76 | setAttr ".tan" 18; 77 | setAttr ".wgt" no; 78 | setAttr -s 2 ".ktv[0:1]" 1 0.2 10 0.2; 79 | createNode animCurveTU -n "CURVE14"; 80 | rename -uid "1A7FD955-44FC-DA80-17C8-F8B2936C2A64"; 81 | setAttr ".tan" 18; 82 | setAttr ".wgt" no; 83 | setAttr -s 2 ".ktv[0:1]" 1 1.4 10 1.4; 84 | createNode animCurveTU -n "CURVE15"; 85 | rename -uid "3F957170-4D7E-FD98-8F3E-6086609AB97F"; 86 | setAttr ".tan" 18; 87 | setAttr ".wgt" no; 88 | setAttr -s 2 ".ktv[0:1]" 1 2.6 10 2.6; 89 | createNode animCurveTU -n "CURVE16"; 90 | rename -uid "2EC19608-4057-B2F8-7796-27BB84CC1532"; 91 | setAttr ".tan" 9; 92 | setAttr ".wgt" no; 93 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 94 | setAttr -s 2 ".kot[0:1]" 5 5; 95 | createNode animCurveTL -n "CURVE18"; 96 | rename -uid "75AC48FC-4740-40A9-B0B6-AFB0FB0FAD85"; 97 | setAttr ".tan" 18; 98 | setAttr ".wgt" no; 99 | setAttr -s 2 ".ktv[0:1]" 1 0 10 15; 100 | // End -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_insert.anim/animation.mb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/mutils/tests/data/test_load_insert.anim/animation.mb -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_insert.anim/pose.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "mayaSceneFile": "C:/Users/hovel/Dropbox/git/site-packages/studiolibrary/packages/mutils/tests/data/test_anim.ma", 4 | "angularUnit": "deg", 5 | "ctime": "1518341545", 6 | "endFrame": 10, 7 | "linearUnit": "cm", 8 | "version": "1.0.0", 9 | "user": "Hovel", 10 | "startFrame": 1, 11 | "timeUnit": "film", 12 | "mayaVersion": "2017" 13 | } 14 | , 15 | "objects": { 16 | "srcSphere:offset": { 17 | "attrs": {} 18 | }, 19 | "srcSphere:sphere": { 20 | "attrs": { 21 | "testInteger": { 22 | "curve": "CURVE1", 23 | "type": "long", 24 | "value": 5 25 | }, 26 | "testProxy": { 27 | "type": "double", 28 | "value": 1.0 29 | }, 30 | "scaleY": { 31 | "type": "double", 32 | "value": 4.66 33 | }, 34 | "testLimit": { 35 | "type": "double", 36 | "value": 10.0 37 | }, 38 | "testConnect": { 39 | "type": "double", 40 | "value": 8.0 41 | }, 42 | "testEnum": { 43 | "curve": "CURVE2", 44 | "type": "enum", 45 | "value": 1 46 | }, 47 | "scaleX": { 48 | "curve": "CURVE3", 49 | "type": "double", 50 | "value": 0.25 51 | }, 52 | "testString": { 53 | "type": "string", 54 | "value": "Hello world" 55 | }, 56 | "scaleZ": { 57 | "curve": "CURVE4", 58 | "type": "double", 59 | "value": 0.5 60 | }, 61 | "rotateX": { 62 | "curve": "CURVE5", 63 | "type": "doubleAngle", 64 | "value": 45.0 65 | }, 66 | "rotateY": { 67 | "type": "doubleAngle", 68 | "value": 50.0 69 | }, 70 | "rotateZ": { 71 | "curve": "CURVE6", 72 | "type": "doubleAngle", 73 | "value": 90.0 74 | }, 75 | "testStatic": { 76 | "type": "double", 77 | "value": 0.0 78 | }, 79 | "translateX": { 80 | "curve": "CURVE7", 81 | "type": "doubleLinear", 82 | "value": 0.0 83 | }, 84 | "translateY": { 85 | "curve": "CURVE8", 86 | "type": "doubleLinear", 87 | "value": 8.0 88 | }, 89 | "translateZ": { 90 | "curve": "CURVE9", 91 | "type": "doubleLinear", 92 | "value": -12.0 93 | }, 94 | "visibility": { 95 | "curve": "CURVE10", 96 | "type": "bool", 97 | "value": true 98 | }, 99 | "testFloat": { 100 | "curve": "CURVE11", 101 | "type": "double", 102 | "value": 0.666 103 | }, 104 | "testAnimated": { 105 | "curve": "CURVE12", 106 | "type": "double", 107 | "value": 0.0 108 | }, 109 | "testVectorX": { 110 | "curve": "CURVE13", 111 | "type": "double", 112 | "value": 0.2 113 | }, 114 | "testVectorY": { 115 | "curve": "CURVE14", 116 | "type": "double", 117 | "value": 1.4 118 | }, 119 | "testVectorZ": { 120 | "curve": "CURVE15", 121 | "type": "double", 122 | "value": 2.6 123 | }, 124 | "testBoolean": { 125 | "curve": "CURVE16", 126 | "type": "bool", 127 | "value": true 128 | } 129 | } 130 | }, 131 | "srcSphere:lockedNode": { 132 | "attrs": { 133 | "translateX": { 134 | "type": "doubleLinear", 135 | "value": 0.0 136 | }, 137 | "translateY": { 138 | "type": "doubleLinear", 139 | "value": 5.0 140 | }, 141 | "translateZ": { 142 | "curve": "CURVE18", 143 | "type": "doubleLinear", 144 | "value": 0.0 145 | }, 146 | "scaleX": { 147 | "type": "double", 148 | "value": 1.0 149 | }, 150 | "scaleY": { 151 | "type": "double", 152 | "value": 1.0 153 | }, 154 | "visibility": { 155 | "type": "bool", 156 | "value": true 157 | }, 158 | "rotateX": { 159 | "type": "doubleAngle", 160 | "value": 0.0 161 | }, 162 | "rotateY": { 163 | "type": "doubleAngle", 164 | "value": 0.0 165 | }, 166 | "rotateZ": { 167 | "type": "doubleAngle", 168 | "value": 0.0 169 | }, 170 | "scaleZ": { 171 | "type": "double", 172 | "value": 1.0 173 | } 174 | } 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace.anim/animation.ma: -------------------------------------------------------------------------------- 1 | //Maya ASCII 2016 scene 2 | //Name: animation.ma 3 | //Last modified: Tue, Aug 09, 2016 08:47:07 AM 4 | //Codeset: 1252 5 | requires maya "2016"; 6 | currentUnit -l centimeter -a degree -t film; 7 | fileInfo "application" "maya"; 8 | fileInfo "product" "Maya 2016"; 9 | fileInfo "version" "2016"; 10 | fileInfo "cutIdentifier" "201502261600-953408"; 11 | fileInfo "osv" "Microsoft Windows 8 Business Edition, 64-bit (Build 9200)\n"; 12 | createNode animCurveTU -n "CURVE1"; 13 | rename -uid "44F21FB5-4121-8FAA-4E5A-2795D8DB1C98"; 14 | setAttr ".tan" 18; 15 | setAttr ".wgt" no; 16 | setAttr -s 2 ".ktv[0:1]" 1 5 10 5; 17 | createNode animCurveTU -n "CURVE2"; 18 | rename -uid "89D65122-4A81-C08C-8F60-A8A69D8BDDFF"; 19 | setAttr ".tan" 9; 20 | setAttr ".wgt" no; 21 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 22 | setAttr -s 2 ".kot[0:1]" 5 5; 23 | createNode animCurveTU -n "CURVE3"; 24 | rename -uid "394F9F8B-44A0-7AEE-3FF8-43BE62C0DDC8"; 25 | setAttr ".tan" 18; 26 | setAttr ".wgt" no; 27 | setAttr -s 2 ".ktv[0:1]" 1 0.25 10 0.42958527814637792; 28 | createNode animCurveTU -n "CURVE4"; 29 | rename -uid "CF90746B-43BD-0210-E0FC-05A5368FC36F"; 30 | setAttr ".tan" 18; 31 | setAttr ".wgt" no; 32 | setAttr -s 2 ".ktv[0:1]" 1 0.5 10 0.85917055629275585; 33 | createNode animCurveTA -n "CURVE5"; 34 | rename -uid "C88B0183-4A49-7472-1F9B-75861AB915A8"; 35 | setAttr ".tan" 18; 36 | setAttr ".wgt" no; 37 | setAttr -s 2 ".ktv[0:1]" 1 45 10 30.81018202226841; 38 | createNode animCurveTA -n "CURVE6"; 39 | rename -uid "70CBACAA-40C4-7C42-B9F5-0BB67E97595D"; 40 | setAttr ".tan" 18; 41 | setAttr ".wgt" no; 42 | setAttr -s 2 ".ktv[0:1]" 1 90 10 149.70880463068096; 43 | createNode animCurveTL -n "CURVE7"; 44 | rename -uid "9A9A4A91-42EF-2651-3696-91ACC6F098B6"; 45 | setAttr ".tan" 18; 46 | setAttr ".wgt" no; 47 | setAttr -s 2 ".ktv[0:1]" 1 0 10 0; 48 | createNode animCurveTL -n "CURVE8"; 49 | rename -uid "CBDCAEFF-4D9E-5162-0CAC-7C8CE46E91F2"; 50 | setAttr ".tan" 18; 51 | setAttr ".wgt" no; 52 | setAttr -s 2 ".ktv[0:1]" 1 8 10 8; 53 | createNode animCurveTL -n "CURVE9"; 54 | rename -uid "B745DFCE-4739-96FC-C827-1494F35ED50D"; 55 | setAttr ".tan" 18; 56 | setAttr ".wgt" no; 57 | setAttr -s 2 ".ktv[0:1]" 1 -12 10 11.214436147065292; 58 | createNode animCurveTU -n "CURVE10"; 59 | rename -uid "0A53795D-45F0-B3C3-E5F2-AAB7665A9E8E"; 60 | setAttr ".tan" 9; 61 | setAttr ".wgt" no; 62 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 63 | setAttr -s 2 ".kot[0:1]" 5 5; 64 | createNode animCurveTU -n "CURVE11"; 65 | rename -uid "7DC24CA9-48A1-3B3A-8278-5A81D70CA4E9"; 66 | setAttr ".tan" 18; 67 | setAttr ".wgt" no; 68 | setAttr -s 2 ".ktv[0:1]" 1 0.666 10 0.666; 69 | createNode animCurveTU -n "CURVE12"; 70 | rename -uid "400DED74-4D52-543D-91D4-0881D938C5C9"; 71 | setAttr ".tan" 18; 72 | setAttr ".wgt" no; 73 | setAttr -s 2 ".ktv[0:1]" 1 0 10 10; 74 | createNode animCurveTU -n "CURVE13"; 75 | rename -uid "5527A666-49BE-9277-3936-12B07312A901"; 76 | setAttr ".tan" 18; 77 | setAttr ".wgt" no; 78 | setAttr -s 2 ".ktv[0:1]" 1 0.2 10 0.2; 79 | createNode animCurveTU -n "CURVE14"; 80 | rename -uid "DF0B2F0E-49A4-61C0-521D-0A8497BF2753"; 81 | setAttr ".tan" 18; 82 | setAttr ".wgt" no; 83 | setAttr -s 2 ".ktv[0:1]" 1 1.4 10 1.4; 84 | createNode animCurveTU -n "CURVE15"; 85 | rename -uid "20CAFE14-4EBD-6A5D-458C-5B82383EC0F2"; 86 | setAttr ".tan" 18; 87 | setAttr ".wgt" no; 88 | setAttr -s 2 ".ktv[0:1]" 1 2.6 10 2.6; 89 | createNode animCurveTU -n "CURVE16"; 90 | rename -uid "7996F54C-49E2-85E5-D444-CBB553D75344"; 91 | setAttr ".tan" 9; 92 | setAttr ".wgt" no; 93 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 94 | setAttr -s 2 ".kot[0:1]" 5 5; 95 | createNode animCurveTL -n "CURVE18"; 96 | rename -uid "C516D5DD-4FA0-4AEC-8E33-0ABF26EF9D60"; 97 | setAttr ".tan" 18; 98 | setAttr ".wgt" no; 99 | setAttr -s 2 ".ktv[0:1]" 1 0 10 15; 100 | // End -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace.anim/animation.mb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/mutils/tests/data/test_load_replace.anim/animation.mb -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace.anim/pose.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "mayaSceneFile": "C:/Users/hovel/Dropbox/git/site-packages/studiolibrary/packages/mutils/tests/data/test_anim.ma", 4 | "angularUnit": "deg", 5 | "ctime": "1518341546", 6 | "endFrame": 10, 7 | "linearUnit": "cm", 8 | "version": "1.0.0", 9 | "user": "Hovel", 10 | "startFrame": 1, 11 | "timeUnit": "film", 12 | "mayaVersion": "2017" 13 | } 14 | , 15 | "objects": { 16 | "srcSphere:offset": { 17 | "attrs": {} 18 | }, 19 | "srcSphere:sphere": { 20 | "attrs": { 21 | "testInteger": { 22 | "curve": "CURVE1", 23 | "type": "long", 24 | "value": 5 25 | }, 26 | "testProxy": { 27 | "type": "double", 28 | "value": 1.0 29 | }, 30 | "scaleY": { 31 | "type": "double", 32 | "value": 4.66 33 | }, 34 | "testLimit": { 35 | "type": "double", 36 | "value": 10.0 37 | }, 38 | "testConnect": { 39 | "type": "double", 40 | "value": 8.0 41 | }, 42 | "testEnum": { 43 | "curve": "CURVE2", 44 | "type": "enum", 45 | "value": 1 46 | }, 47 | "scaleX": { 48 | "curve": "CURVE3", 49 | "type": "double", 50 | "value": 0.25 51 | }, 52 | "testString": { 53 | "type": "string", 54 | "value": "Hello world" 55 | }, 56 | "scaleZ": { 57 | "curve": "CURVE4", 58 | "type": "double", 59 | "value": 0.5 60 | }, 61 | "rotateX": { 62 | "curve": "CURVE5", 63 | "type": "doubleAngle", 64 | "value": 45.0 65 | }, 66 | "rotateY": { 67 | "type": "doubleAngle", 68 | "value": 50.0 69 | }, 70 | "rotateZ": { 71 | "curve": "CURVE6", 72 | "type": "doubleAngle", 73 | "value": 90.0 74 | }, 75 | "testStatic": { 76 | "type": "double", 77 | "value": 0.0 78 | }, 79 | "translateX": { 80 | "curve": "CURVE7", 81 | "type": "doubleLinear", 82 | "value": 0.0 83 | }, 84 | "translateY": { 85 | "curve": "CURVE8", 86 | "type": "doubleLinear", 87 | "value": 8.0 88 | }, 89 | "translateZ": { 90 | "curve": "CURVE9", 91 | "type": "doubleLinear", 92 | "value": -12.0 93 | }, 94 | "visibility": { 95 | "curve": "CURVE10", 96 | "type": "bool", 97 | "value": true 98 | }, 99 | "testFloat": { 100 | "curve": "CURVE11", 101 | "type": "double", 102 | "value": 0.666 103 | }, 104 | "testAnimated": { 105 | "curve": "CURVE12", 106 | "type": "double", 107 | "value": 0.0 108 | }, 109 | "testVectorX": { 110 | "curve": "CURVE13", 111 | "type": "double", 112 | "value": 0.2 113 | }, 114 | "testVectorY": { 115 | "curve": "CURVE14", 116 | "type": "double", 117 | "value": 1.4 118 | }, 119 | "testVectorZ": { 120 | "curve": "CURVE15", 121 | "type": "double", 122 | "value": 2.6 123 | }, 124 | "testBoolean": { 125 | "curve": "CURVE16", 126 | "type": "bool", 127 | "value": true 128 | } 129 | } 130 | }, 131 | "srcSphere:lockedNode": { 132 | "attrs": { 133 | "translateX": { 134 | "type": "doubleLinear", 135 | "value": 0.0 136 | }, 137 | "translateY": { 138 | "type": "doubleLinear", 139 | "value": 5.0 140 | }, 141 | "translateZ": { 142 | "curve": "CURVE18", 143 | "type": "doubleLinear", 144 | "value": 0.0 145 | }, 146 | "scaleX": { 147 | "type": "double", 148 | "value": 1.0 149 | }, 150 | "scaleY": { 151 | "type": "double", 152 | "value": 1.0 153 | }, 154 | "visibility": { 155 | "type": "bool", 156 | "value": true 157 | }, 158 | "rotateX": { 159 | "type": "doubleAngle", 160 | "value": 0.0 161 | }, 162 | "rotateY": { 163 | "type": "doubleAngle", 164 | "value": 0.0 165 | }, 166 | "rotateZ": { 167 | "type": "doubleAngle", 168 | "value": 0.0 169 | }, 170 | "scaleZ": { 171 | "type": "double", 172 | "value": 1.0 173 | } 174 | } 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace_completely.anim/animation.ma: -------------------------------------------------------------------------------- 1 | //Maya ASCII 2016 scene 2 | //Name: animation.ma 3 | //Last modified: Tue, Aug 09, 2016 08:47:08 AM 4 | //Codeset: 1252 5 | requires maya "2016"; 6 | currentUnit -l centimeter -a degree -t film; 7 | fileInfo "application" "maya"; 8 | fileInfo "product" "Maya 2016"; 9 | fileInfo "version" "2016"; 10 | fileInfo "cutIdentifier" "201502261600-953408"; 11 | fileInfo "osv" "Microsoft Windows 8 Business Edition, 64-bit (Build 9200)\n"; 12 | createNode animCurveTU -n "CURVE1"; 13 | rename -uid "07400D46-41EF-56B5-ABE2-FCB3FDC705F2"; 14 | setAttr ".tan" 18; 15 | setAttr ".wgt" no; 16 | setAttr -s 2 ".ktv[0:1]" 1 5 10 5; 17 | createNode animCurveTU -n "CURVE2"; 18 | rename -uid "E2B997BA-4A97-2EF7-C65E-E7BD483688D6"; 19 | setAttr ".tan" 9; 20 | setAttr ".wgt" no; 21 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 22 | setAttr -s 2 ".kot[0:1]" 5 5; 23 | createNode animCurveTU -n "CURVE3"; 24 | rename -uid "2AF61E8F-4817-A910-3417-6B9823CED36D"; 25 | setAttr ".tan" 18; 26 | setAttr ".wgt" no; 27 | setAttr -s 2 ".ktv[0:1]" 1 0.25 10 0.42958527814637792; 28 | createNode animCurveTU -n "CURVE4"; 29 | rename -uid "61ED901E-4BFA-A417-6F1A-A89854BAE1F9"; 30 | setAttr ".tan" 18; 31 | setAttr ".wgt" no; 32 | setAttr -s 2 ".ktv[0:1]" 1 0.5 10 0.85917055629275585; 33 | createNode animCurveTA -n "CURVE5"; 34 | rename -uid "25E543E0-4233-B654-6DC2-C980CC9ED600"; 35 | setAttr ".tan" 18; 36 | setAttr ".wgt" no; 37 | setAttr -s 2 ".ktv[0:1]" 1 45 10 30.81018202226841; 38 | createNode animCurveTA -n "CURVE6"; 39 | rename -uid "3921DD56-44E6-E6D7-DB1B-C28209F3157E"; 40 | setAttr ".tan" 18; 41 | setAttr ".wgt" no; 42 | setAttr -s 2 ".ktv[0:1]" 1 90 10 149.70880463068096; 43 | createNode animCurveTL -n "CURVE7"; 44 | rename -uid "5EE5D458-498D-AD10-196B-0F99DE4A9F97"; 45 | setAttr ".tan" 18; 46 | setAttr ".wgt" no; 47 | setAttr -s 2 ".ktv[0:1]" 1 0 10 0; 48 | createNode animCurveTL -n "CURVE8"; 49 | rename -uid "81FBC4EF-479F-58C1-D24D-859C16E01BA1"; 50 | setAttr ".tan" 18; 51 | setAttr ".wgt" no; 52 | setAttr -s 2 ".ktv[0:1]" 1 8 10 8; 53 | createNode animCurveTL -n "CURVE9"; 54 | rename -uid "898B5C26-448D-366F-0E63-5082268110FB"; 55 | setAttr ".tan" 18; 56 | setAttr ".wgt" no; 57 | setAttr -s 2 ".ktv[0:1]" 1 -12 10 11.214436147065292; 58 | createNode animCurveTU -n "CURVE10"; 59 | rename -uid "139725A4-4C9A-C99F-2E9F-AF91BC3F67E9"; 60 | setAttr ".tan" 9; 61 | setAttr ".wgt" no; 62 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 63 | setAttr -s 2 ".kot[0:1]" 5 5; 64 | createNode animCurveTU -n "CURVE11"; 65 | rename -uid "1A5281F1-4252-087B-1631-9998C170D33B"; 66 | setAttr ".tan" 18; 67 | setAttr ".wgt" no; 68 | setAttr -s 2 ".ktv[0:1]" 1 0.666 10 0.666; 69 | createNode animCurveTU -n "CURVE12"; 70 | rename -uid "3B543491-429F-763A-0075-4EB85B2E50AB"; 71 | setAttr ".tan" 18; 72 | setAttr ".wgt" no; 73 | setAttr -s 2 ".ktv[0:1]" 1 0 10 10; 74 | createNode animCurveTU -n "CURVE13"; 75 | rename -uid "8532CE01-49F8-571C-2D65-A98F3427EBF0"; 76 | setAttr ".tan" 18; 77 | setAttr ".wgt" no; 78 | setAttr -s 2 ".ktv[0:1]" 1 0.2 10 0.2; 79 | createNode animCurveTU -n "CURVE14"; 80 | rename -uid "D9767D2E-4DFC-F089-183D-789D8B697CF1"; 81 | setAttr ".tan" 18; 82 | setAttr ".wgt" no; 83 | setAttr -s 2 ".ktv[0:1]" 1 1.4 10 1.4; 84 | createNode animCurveTU -n "CURVE15"; 85 | rename -uid "73706920-4309-DAEE-AE70-CAAF4C1D68FB"; 86 | setAttr ".tan" 18; 87 | setAttr ".wgt" no; 88 | setAttr -s 2 ".ktv[0:1]" 1 2.6 10 2.6; 89 | createNode animCurveTU -n "CURVE16"; 90 | rename -uid "70D851D4-43F8-72F5-0327-E28AEB0F174C"; 91 | setAttr ".tan" 9; 92 | setAttr ".wgt" no; 93 | setAttr -s 2 ".ktv[0:1]" 1 1 10 1; 94 | setAttr -s 2 ".kot[0:1]" 5 5; 95 | createNode animCurveTL -n "CURVE18"; 96 | rename -uid "716232B6-4903-7906-472A-569BBA3146DA"; 97 | setAttr ".tan" 18; 98 | setAttr ".wgt" no; 99 | setAttr -s 2 ".ktv[0:1]" 1 0 10 15; 100 | // End -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace_completely.anim/animation.mb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/mutils/tests/data/test_load_replace_completely.anim/animation.mb -------------------------------------------------------------------------------- /src/mutils/tests/data/test_load_replace_completely.anim/pose.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "mayaSceneFile": "C:/Users/hovel/Dropbox/git/site-packages/studiolibrary/packages/mutils/tests/data/test_anim.ma", 4 | "angularUnit": "deg", 5 | "ctime": "1518341546", 6 | "endFrame": 10, 7 | "linearUnit": "cm", 8 | "version": "1.0.0", 9 | "user": "Hovel", 10 | "startFrame": 1, 11 | "timeUnit": "film", 12 | "mayaVersion": "2017" 13 | } 14 | , 15 | "objects": { 16 | "srcSphere:offset": { 17 | "attrs": {} 18 | }, 19 | "srcSphere:sphere": { 20 | "attrs": { 21 | "testInteger": { 22 | "curve": "CURVE1", 23 | "type": "long", 24 | "value": 5 25 | }, 26 | "testProxy": { 27 | "type": "double", 28 | "value": 1.0 29 | }, 30 | "scaleY": { 31 | "type": "double", 32 | "value": 4.66 33 | }, 34 | "testLimit": { 35 | "type": "double", 36 | "value": 10.0 37 | }, 38 | "testConnect": { 39 | "type": "double", 40 | "value": 8.0 41 | }, 42 | "testEnum": { 43 | "curve": "CURVE2", 44 | "type": "enum", 45 | "value": 1 46 | }, 47 | "scaleX": { 48 | "curve": "CURVE3", 49 | "type": "double", 50 | "value": 0.25 51 | }, 52 | "testString": { 53 | "type": "string", 54 | "value": "Hello world" 55 | }, 56 | "scaleZ": { 57 | "curve": "CURVE4", 58 | "type": "double", 59 | "value": 0.5 60 | }, 61 | "rotateX": { 62 | "curve": "CURVE5", 63 | "type": "doubleAngle", 64 | "value": 45.0 65 | }, 66 | "rotateY": { 67 | "type": "doubleAngle", 68 | "value": 50.0 69 | }, 70 | "rotateZ": { 71 | "curve": "CURVE6", 72 | "type": "doubleAngle", 73 | "value": 90.0 74 | }, 75 | "testStatic": { 76 | "type": "double", 77 | "value": 0.0 78 | }, 79 | "translateX": { 80 | "curve": "CURVE7", 81 | "type": "doubleLinear", 82 | "value": 0.0 83 | }, 84 | "translateY": { 85 | "curve": "CURVE8", 86 | "type": "doubleLinear", 87 | "value": 8.0 88 | }, 89 | "translateZ": { 90 | "curve": "CURVE9", 91 | "type": "doubleLinear", 92 | "value": -12.0 93 | }, 94 | "visibility": { 95 | "curve": "CURVE10", 96 | "type": "bool", 97 | "value": true 98 | }, 99 | "testFloat": { 100 | "curve": "CURVE11", 101 | "type": "double", 102 | "value": 0.666 103 | }, 104 | "testAnimated": { 105 | "curve": "CURVE12", 106 | "type": "double", 107 | "value": 0.0 108 | }, 109 | "testVectorX": { 110 | "curve": "CURVE13", 111 | "type": "double", 112 | "value": 0.2 113 | }, 114 | "testVectorY": { 115 | "curve": "CURVE14", 116 | "type": "double", 117 | "value": 1.4 118 | }, 119 | "testVectorZ": { 120 | "curve": "CURVE15", 121 | "type": "double", 122 | "value": 2.6 123 | }, 124 | "testBoolean": { 125 | "curve": "CURVE16", 126 | "type": "bool", 127 | "value": true 128 | } 129 | } 130 | }, 131 | "srcSphere:lockedNode": { 132 | "attrs": { 133 | "translateX": { 134 | "type": "doubleLinear", 135 | "value": 0.0 136 | }, 137 | "translateY": { 138 | "type": "doubleLinear", 139 | "value": 5.0 140 | }, 141 | "translateZ": { 142 | "curve": "CURVE18", 143 | "type": "doubleLinear", 144 | "value": 0.0 145 | }, 146 | "scaleX": { 147 | "type": "double", 148 | "value": 1.0 149 | }, 150 | "scaleY": { 151 | "type": "double", 152 | "value": 1.0 153 | }, 154 | "visibility": { 155 | "type": "bool", 156 | "value": true 157 | }, 158 | "rotateX": { 159 | "type": "doubleAngle", 160 | "value": 0.0 161 | }, 162 | "rotateY": { 163 | "type": "doubleAngle", 164 | "value": 0.0 165 | }, 166 | "rotateZ": { 167 | "type": "doubleAngle", 168 | "value": 0.0 169 | }, 170 | "scaleZ": { 171 | "type": "double", 172 | "value": 1.0 173 | } 174 | } 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_non_unique_names.pose: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "mayaSceneFile": "C:/Users/hovel/Dropbox/git/site-packages/studiolibrary/packages/mutils/tests/data/test_non_unique_names.ma", 4 | "version": "1.0.0", 5 | "user": "Hovel", 6 | "mayaVersion": "2017", 7 | "ctime": "1518341544" 8 | } 9 | , 10 | "objects": { 11 | "srcSphere:offset": { 12 | "attrs": {} 13 | }, 14 | "srcSphere:sphere": { 15 | "attrs": { 16 | "testInteger": { 17 | "type": "long", 18 | "value": 5 19 | }, 20 | "testProxy": { 21 | "type": "double", 22 | "value": 1.0 23 | }, 24 | "scaleY": { 25 | "type": "double", 26 | "value": 4.66 27 | }, 28 | "testLimit": { 29 | "type": "double", 30 | "value": 10.0 31 | }, 32 | "testConnect": { 33 | "type": "double", 34 | "value": 8.0 35 | }, 36 | "testEnum": { 37 | "type": "enum", 38 | "value": 1 39 | }, 40 | "scaleX": { 41 | "type": "double", 42 | "value": 0.25 43 | }, 44 | "testString": { 45 | "type": "string", 46 | "value": "Hello world" 47 | }, 48 | "scaleZ": { 49 | "type": "double", 50 | "value": 0.5 51 | }, 52 | "rotateX": { 53 | "type": "doubleAngle", 54 | "value": 45.0 55 | }, 56 | "rotateY": { 57 | "type": "doubleAngle", 58 | "value": 50.0 59 | }, 60 | "rotateZ": { 61 | "type": "doubleAngle", 62 | "value": 90.0 63 | }, 64 | "translateX": { 65 | "type": "doubleLinear", 66 | "value": 0.0 67 | }, 68 | "translateY": { 69 | "type": "doubleLinear", 70 | "value": 8.0 71 | }, 72 | "translateZ": { 73 | "type": "doubleLinear", 74 | "value": -12.0 75 | }, 76 | "visibility": { 77 | "type": "bool", 78 | "value": true 79 | }, 80 | "testFloat": { 81 | "type": "double", 82 | "value": 0.666 83 | }, 84 | "testAnimated": { 85 | "type": "double", 86 | "value": 0.0 87 | }, 88 | "testVectorX": { 89 | "type": "double", 90 | "value": 0.2 91 | }, 92 | "testVectorY": { 93 | "type": "double", 94 | "value": 1.4 95 | }, 96 | "testVectorZ": { 97 | "type": "double", 98 | "value": 2.6 99 | }, 100 | "testBoolean": { 101 | "type": "bool", 102 | "value": true 103 | } 104 | } 105 | }, 106 | "srcSphere:lockedNode": { 107 | "attrs": { 108 | "translateX": { 109 | "type": "doubleLinear", 110 | "value": 0.0 111 | }, 112 | "translateY": { 113 | "type": "doubleLinear", 114 | "value": 5.0 115 | }, 116 | "translateZ": { 117 | "type": "doubleLinear", 118 | "value": 0.0 119 | }, 120 | "scaleX": { 121 | "type": "double", 122 | "value": 1.0 123 | }, 124 | "scaleY": { 125 | "type": "double", 126 | "value": 1.0 127 | }, 128 | "visibility": { 129 | "type": "bool", 130 | "value": true 131 | }, 132 | "rotateX": { 133 | "type": "doubleAngle", 134 | "value": 0.0 135 | }, 136 | "rotateY": { 137 | "type": "doubleAngle", 138 | "value": 0.0 139 | }, 140 | "rotateZ": { 141 | "type": "doubleAngle", 142 | "value": 0.0 143 | }, 144 | "scaleZ": { 145 | "type": "double", 146 | "value": 1.0 147 | } 148 | } 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_older_version.anim/.studioLibrary/record.dict: -------------------------------------------------------------------------------- 1 | {'end': 24, 'description': 'eded', 'scene': '', 'start': 1, 'mtime': '1391012417', 'owner': 'tlhomme', 'ctime': '1391012417'} -------------------------------------------------------------------------------- /src/mutils/tests/data/test_older_version.dict: -------------------------------------------------------------------------------- 1 | {'srcSphere:sphere': { 2 | 'rotateX': ('doubleAngle', 45.0), 3 | 'testEnum': ('enum', 1), 4 | 'translateX': ('doubleLinear', 0.0), 5 | 'translateY': ('doubleLinear', 8.0), 6 | 'translateZ': ('doubleLinear', -12.0), 7 | 'rotateY': ('doubleAngle', 50.0), 8 | 'testFloat': ('double', 0.66600000000000004), 9 | 'testAnimated': ('double', 0.0), 10 | 'scaleX': ('double', 0.25), 11 | 'scaleY': ('double', 4.6600000000000001), 12 | 'visibility': ('bool', True), 13 | 'testString': ('string', u'Hello world'), 14 | 'testVectorX': ('double', 0.20000000000000001), 15 | 'testVectorY': ('double', 1.3999999999999999), 16 | 'rotateZ': ('doubleAngle', 90.0), 17 | 'testVectorZ': ('double', 2.6000000000000001), 18 | 'scaleZ': ('double', 0.5), 19 | 'testInteger': ('long', 5), 20 | 'testBoolean': ('bool', True), 21 | 'testConnect': ('double', 8.0) 22 | }, 23 | 'srcSphere:lockedNode': { 24 | 'translateX': ('doubleLinear', 0.0), 25 | 'translateY': ('doubleLinear', 5.0), 26 | 'translateZ': ('doubleLinear', 0.0), 27 | 'scaleX': ('double', 1.0), 28 | 'scaleY': ('double', 1.0), 29 | 'visibility': ('bool', True), 30 | 'rotateX': ('doubleAngle', 0.0), 31 | 'rotateY': ('doubleAngle', 0.0), 32 | 'rotateZ': ('doubleAngle', 0.0), 33 | 'scaleZ': ('double', 1.0) 34 | }} -------------------------------------------------------------------------------- /src/mutils/tests/data/test_pose.pose: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "mayaSceneFile": "C:/Users/hovel/Dropbox/git/site-packages/studiolibrary/packages/mutils/tests/data/test_pose.ma", 4 | "version": "1.0.0", 5 | "user": "Hovel", 6 | "mayaVersion": "2017", 7 | "ctime": "1518341544" 8 | } 9 | , 10 | "objects": { 11 | "srcSphere:offset": { 12 | "attrs": {} 13 | }, 14 | "srcSphere:sphere": { 15 | "attrs": { 16 | "testInteger": { 17 | "type": "long", 18 | "value": 5 19 | }, 20 | "testProxy": { 21 | "type": "double", 22 | "value": 1.0 23 | }, 24 | "scaleY": { 25 | "type": "double", 26 | "value": 1.5 27 | }, 28 | "testLimit": { 29 | "type": "double", 30 | "value": 10.0 31 | }, 32 | "testConnect": { 33 | "type": "double", 34 | "value": 8.0 35 | }, 36 | "testEnum": { 37 | "type": "enum", 38 | "value": 1 39 | }, 40 | "scaleX": { 41 | "type": "double", 42 | "value": 0.25 43 | }, 44 | "testString": { 45 | "type": "string", 46 | "value": "Hello world" 47 | }, 48 | "scaleZ": { 49 | "type": "double", 50 | "value": 0.5 51 | }, 52 | "rotateX": { 53 | "type": "doubleAngle", 54 | "value": 45.0 55 | }, 56 | "rotateY": { 57 | "type": "doubleAngle", 58 | "value": 25.0 59 | }, 60 | "rotateZ": { 61 | "type": "doubleAngle", 62 | "value": 90.0 63 | }, 64 | "translateX": { 65 | "type": "doubleLinear", 66 | "value": 0.0 67 | }, 68 | "translateY": { 69 | "type": "doubleLinear", 70 | "value": 8.0 71 | }, 72 | "translateZ": { 73 | "type": "doubleLinear", 74 | "value": -12.0 75 | }, 76 | "visibility": { 77 | "type": "bool", 78 | "value": true 79 | }, 80 | "testFloat": { 81 | "type": "double", 82 | "value": 0.666 83 | }, 84 | "testAnimated": { 85 | "type": "double", 86 | "value": 0.0 87 | }, 88 | "testVectorX": { 89 | "type": "double", 90 | "value": 0.2 91 | }, 92 | "testVectorY": { 93 | "type": "double", 94 | "value": 1.4 95 | }, 96 | "testVectorZ": { 97 | "type": "double", 98 | "value": 2.6 99 | }, 100 | "testBoolean": { 101 | "type": "bool", 102 | "value": true 103 | } 104 | } 105 | }, 106 | "srcSphere:lockedNode": { 107 | "attrs": { 108 | "translateX": { 109 | "type": "doubleLinear", 110 | "value": 0.0 111 | }, 112 | "translateY": { 113 | "type": "doubleLinear", 114 | "value": 0.0 115 | }, 116 | "translateZ": { 117 | "type": "doubleLinear", 118 | "value": 0.0 119 | }, 120 | "scaleX": { 121 | "type": "double", 122 | "value": 1.0 123 | }, 124 | "scaleY": { 125 | "type": "double", 126 | "value": 1.0 127 | }, 128 | "visibility": { 129 | "type": "bool", 130 | "value": true 131 | }, 132 | "rotateX": { 133 | "type": "doubleAngle", 134 | "value": 0.0 135 | }, 136 | "rotateY": { 137 | "type": "doubleAngle", 138 | "value": 0.0 139 | }, 140 | "rotateZ": { 141 | "type": "doubleAngle", 142 | "value": 0.0 143 | }, 144 | "scaleZ": { 145 | "type": "double", 146 | "value": 1.0 147 | } 148 | } 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /src/mutils/tests/data/test_poseplugin.pose/.studiolibrary/record.dict: -------------------------------------------------------------------------------- 1 | {'owner': 'hovel', 'ctime': '1437148163', 'mtime': '1437148163'} -------------------------------------------------------------------------------- /src/mutils/tests/run.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | """ 13 | # Example: 14 | # RUN TEST SUITE 15 | import mutils.tests 16 | reload(mutils.tests) 17 | mutils.tests.run() 18 | """ 19 | import unittest 20 | 21 | import logging 22 | 23 | 24 | logging.basicConfig( 25 | filemode='w', 26 | level=logging.DEBUG, 27 | format='%(levelname)s: %(funcName)s: %(message)s', 28 | ) 29 | 30 | 31 | def testSuite(): 32 | """ 33 | Return a test suite containing all the tests. 34 | 35 | :rtype: unittest.TestSuite 36 | """ 37 | import test_pose 38 | import test_anim 39 | import test_match 40 | import test_utils 41 | import test_attribute 42 | import test_mirrortable 43 | 44 | suite = unittest.TestSuite() 45 | 46 | s = unittest.makeSuite(test_pose.TestPose, 'test') 47 | suite.addTest(s) 48 | 49 | s = unittest.makeSuite(test_anim.TestAnim, 'test') 50 | suite.addTest(s) 51 | 52 | s = unittest.makeSuite(test_utils.TestUtils, 'test') 53 | suite.addTest(s) 54 | 55 | s = unittest.makeSuite(test_match.TestMatch, 'test') 56 | suite.addTest(s) 57 | 58 | s = unittest.makeSuite(test_attribute.TestAttribute, 'test') 59 | suite.addTest(s) 60 | 61 | s = unittest.makeSuite(test_mirrortable.TestMirrorTable, 'test') 62 | suite.addTest(s) 63 | 64 | return suite 65 | 66 | 67 | def run(): 68 | """ 69 | Call from within Maya to run all valid tests. 70 | """ 71 | import mutils.animation 72 | mutils.animation.FIX_SAVE_ANIM_REFERENCE_LOCKED_ERROR = True 73 | 74 | tests = unittest.TextTestRunner() 75 | tests.run(testSuite()) 76 | -------------------------------------------------------------------------------- /src/mutils/tests/test_anim.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import unittest 14 | 15 | import mutils 16 | 17 | import test_base 18 | 19 | 20 | class TestAnim(test_base.TestBase): 21 | 22 | def setUp(self): 23 | """ 24 | """ 25 | 26 | def save(self, bakeConnected=False): 27 | """ 28 | Test saving the animation to disc. 29 | """ 30 | self.open() 31 | anim = mutils.Animation.fromObjects(self.srcObjects) 32 | anim.save(self.dstPath, bakeConnected=bakeConnected) 33 | 34 | # def test_older_version(self): 35 | # """ 36 | # Test animation parser for an older animation format 37 | # """ 38 | # srcPath = self.dataPath("test_older_version.anim") 39 | # a = mutils.Animation.fromPath(srcPath) 40 | 41 | def test_load_replace_completely(self): 42 | """ 43 | Test loading the animation with replace completely option. 44 | """ 45 | self.srcPath = self.dataPath("test_anim.ma") 46 | self.dstPath = self.dataPath("test_load_replace_completely.anim") 47 | self.save() 48 | 49 | anim = mutils.Animation.fromPath(self.dstPath) 50 | anim.load(self.dstObjects) 51 | 52 | self.assertEqualAnimation() 53 | 54 | def test_bake_connected(self): 55 | """ 56 | Test saving animation with the option bake connected. 57 | """ 58 | srcPath = self.dataPath("test_bake_connected.ma") 59 | dstPath = self.dataPath("test_bake_connected.anim") 60 | 61 | srcObjects = [ 62 | "srcSphere:group", 63 | "srcSphere:lockedNode", 64 | "srcSphere:offset", 65 | "srcSphere:sphere" 66 | ] 67 | 68 | dstObjects = [ 69 | "dstSphere:group", 70 | "dstSphere:lockedNode", 71 | "dstSphere:offset", 72 | "dstSphere:sphere" 73 | ] 74 | 75 | self.open(path=srcPath) 76 | 77 | anim = mutils.Animation.fromObjects(srcObjects) 78 | anim.save(dstPath, bakeConnected=True) 79 | 80 | anim = mutils.Animation.fromPath(dstPath) 81 | anim.load(dstObjects) 82 | 83 | self.assertEqualAnimation() 84 | 85 | def test_load_replace(self): 86 | """ 87 | Test loading the animation with the option Replace. 88 | """ 89 | self.srcPath = self.dataPath("test_anim.ma") 90 | self.dstPath = self.dataPath("test_load_replace.anim") 91 | self.save() 92 | 93 | anim = mutils.Animation.fromPath(self.dstPath) 94 | anim.load(self.dstObjects, option=mutils.PasteOption.Replace, startFrame=5) 95 | 96 | def test_load_insert(self): 97 | """ 98 | Test loading the animation with the option Insert. 99 | """ 100 | self.srcPath = self.dataPath("test_anim.ma") 101 | self.dstPath = self.dataPath("test_load_insert.anim") 102 | self.save() 103 | 104 | anim = mutils.Animation.fromPath(self.dstPath) 105 | anim.load(self.dstObjects, option=mutils.PasteOption.Insert, startFrame=5) 106 | 107 | 108 | def testSuite(): 109 | """ 110 | Return the test suite for the test case. 111 | 112 | :rtype: unittest.TestSuite 113 | """ 114 | suite = unittest.TestSuite() 115 | s = unittest.makeSuite(TestAnim, 'test') 116 | suite.addTest(s) 117 | return suite 118 | 119 | 120 | def run(): 121 | """ 122 | Call from within Maya to run all valid tests. 123 | """ 124 | tests = unittest.TextTestRunner() 125 | tests.run(testSuite()) 126 | -------------------------------------------------------------------------------- /src/mutils/tests/test_base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | import unittest 15 | 16 | import maya.cmds 17 | 18 | import mutils 19 | 20 | 21 | TEST_DATA_DIR = os.path.join(os.path.dirname(mutils.__file__), "tests", "data") 22 | 23 | 24 | class TestBase(unittest.TestCase): 25 | 26 | def __init__(self, *args): 27 | """ 28 | :type args: list 29 | """ 30 | unittest.TestCase.__init__(self, *args) 31 | 32 | self.srcPath = os.path.join(TEST_DATA_DIR, "test_pose.ma") 33 | self.dstPath = os.path.join(TEST_DATA_DIR, "test_pose.pose") 34 | 35 | self.srcObjects = [ 36 | "srcSphere:lockedNode", 37 | "srcSphere:offset", 38 | "srcSphere:sphere", 39 | ] 40 | 41 | self.dstObjects = [ 42 | "dstSphere:lockedNode", 43 | "dstSphere:offset", 44 | "dstSphere:sphere", 45 | ] 46 | 47 | self.srcNamespaces = ["srcSphere"] 48 | self.dstNamespaces = ["dstSphere"] 49 | 50 | def dataDir(self): 51 | """ 52 | Return the location on disc to the test data. 53 | 54 | :rtype: str 55 | """ 56 | return os.path.join(os.path.dirname(mutils.__file__), "tests", "data") 57 | 58 | def dataPath(self, fileName): 59 | """ 60 | Return data location with the given fileName. 61 | 62 | :rtype: str 63 | """ 64 | return os.path.join(self.dataDir(), fileName) 65 | 66 | def open(self, path=None): 67 | """ 68 | Open the specified file in Maya. 69 | 70 | :type path: str | None 71 | """ 72 | if path is None: 73 | path = self.srcPath 74 | 75 | maya.cmds.file( 76 | path, 77 | open=True, 78 | force=True, 79 | ignoreVersion=True, 80 | executeScriptNodes=False, 81 | ) 82 | 83 | def listAttr(self, srcObjects=None, dstObjects=None): 84 | """ 85 | Return the source & destination attributes for the given objects. 86 | 87 | :rtype: list[(mutils.Attribute, mutils.Attribute)] 88 | """ 89 | attrs = [] 90 | srcObjects = srcObjects or self.srcObjects 91 | dstObjects = dstObjects or self.dstObjects 92 | 93 | for i, srcObj in enumerate(srcObjects): 94 | srcObj = srcObjects[i] 95 | dstObj = dstObjects[i] 96 | 97 | srcAttrs = maya.cmds.listAttr(srcObj, keyable=True, unlocked=True, scalar=True) or [] 98 | 99 | for srcAttr in srcAttrs: 100 | srcAttribute = mutils.Attribute(srcObj, srcAttr) 101 | dstAttribute = mutils.Attribute(dstObj, srcAttr) 102 | attrs.append((srcAttribute, dstAttribute)) 103 | 104 | return attrs 105 | 106 | def assertEqualAnimation( 107 | self, 108 | srcObjects=None, 109 | dstObjects=None, 110 | ): 111 | """ 112 | Test that the animation for the given objects is equal. 113 | 114 | If the animation curves do not compare equal, the test will fail. 115 | 116 | :type srcObjects: list[str] | None 117 | :type dstObjects: list[str] | None 118 | """ 119 | for frame in [1, 10, 24]: 120 | maya.cmds.currentTime(frame) 121 | self.assertEqualAttributeValues(srcObjects, dstObjects) 122 | 123 | def assertEqualAttributeValues( 124 | self, 125 | srcObjects=None, 126 | dstObjects=None, 127 | ): 128 | """ 129 | Test that the attribute values for the given objects are equal. 130 | 131 | If the values do not compare equal, the test will fail. 132 | 133 | :type srcObjects: list[str] | None 134 | :type dstObjects: list[str] | None 135 | """ 136 | for srcAttribute, dstAttribute in self.listAttr(srcObjects, dstObjects): 137 | 138 | if not dstAttribute.exists(): 139 | continue 140 | 141 | msg = "Attribute value is not equal! {0} != {1}" 142 | msg = msg.format(srcAttribute.fullname(), dstAttribute.fullname()) 143 | self.assertEqual(srcAttribute.value(), dstAttribute.value(), msg) 144 | -------------------------------------------------------------------------------- /src/mutils/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import unittest 14 | 15 | import mutils.animation 16 | 17 | 18 | class TestUtils(unittest.TestCase): 19 | 20 | def test_clamp_range(self): 21 | """ 22 | Test the clamp range command 23 | """ 24 | msg = "Incorrect clamp range" 25 | 26 | # Test clamp range on both min and max 27 | clampRange = mutils.animation.clampRange((15, 35), (20, 30)) 28 | assert (20, 30) == clampRange, msg 29 | 30 | # Test clamp range on min time 31 | clampRange = mutils.animation.clampRange((10, 25), (20, 30)) 32 | assert (20, 25) == clampRange, msg 33 | 34 | # Test clamp range on man time 35 | clampRange = mutils.animation.clampRange((25, 40), (20, 30)) 36 | assert (25, 30) == clampRange, msg 37 | 38 | # Test min out of bounds error 39 | def test_exception(): 40 | clampRange = mutils.animation.clampRange((5, 15), (20, 30)) 41 | self.assertRaises(mutils.animation.OutOfBoundsError, test_exception) 42 | 43 | # Test max out of bounds error 44 | def test_exception(): 45 | clampRange = mutils.animation.clampRange((65, 95), (20, 30)) 46 | self.assertRaises(mutils.animation.OutOfBoundsError, test_exception) 47 | 48 | 49 | def testSuite(): 50 | """ 51 | Return the test suite for this module. 52 | 53 | :rtype: unittest.TestSuite 54 | """ 55 | suite = unittest.TestSuite() 56 | s = unittest.makeSuite(TestUtils, 'test') 57 | suite.addTest(s) 58 | return suite 59 | 60 | 61 | def run(): 62 | """ 63 | Call from within Maya to run all valid tests. 64 | 65 | Example: 66 | 67 | import mutils.tests.test_attribute 68 | reload(mutils.tests.test_attribute) 69 | mutils.tests.test_attribute.run() 70 | """ 71 | tests = unittest.TextTestRunner() 72 | tests.run(testSuite()) 73 | -------------------------------------------------------------------------------- /src/studiolibrary/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | __version__ = "2.20.2" 14 | 15 | 16 | def version(): 17 | """ 18 | Return the current version of the Studio Library 19 | 20 | :rtype: str 21 | """ 22 | return __version__ 23 | 24 | 25 | from studiolibrary import config 26 | from studiolibrary import resource 27 | from studiolibrary.utils import * 28 | from studiolibrary.library import Library 29 | from studiolibrary.libraryitem import LibraryItem 30 | from studiolibrary.main import main 31 | -------------------------------------------------------------------------------- /src/studiolibrary/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | import json 15 | 16 | 17 | _config = None 18 | 19 | 20 | def get(*args): 21 | """ 22 | Get values from the config. 23 | 24 | :rtype: str 25 | """ 26 | global _config 27 | 28 | if not _config: 29 | _config = read(paths()) 30 | 31 | return _config.get(*args) 32 | 33 | 34 | def set(key, value): 35 | 36 | global _config 37 | 38 | if not _config: 39 | _config = read(paths()) 40 | 41 | _config[key] = value 42 | 43 | 44 | def paths(): 45 | """ 46 | Return all possible config paths. 47 | 48 | :rtype: list[str] 49 | """ 50 | cwd = os.path.dirname(__file__) 51 | paths_ = [os.path.join(cwd, "config", "default.json")] 52 | 53 | path = os.environ.get("STUDIO_LIBRARY_CONFIG_PATH") 54 | path = path or os.path.join(cwd, "config", "config.json") 55 | 56 | if not os.path.exists(path): 57 | cwd = os.path.dirname(os.path.dirname(cwd)) 58 | path = os.path.join(cwd, "config", "config.json") 59 | 60 | if os.path.exists(path): 61 | paths_.append(path) 62 | 63 | return paths_ 64 | 65 | 66 | def read(paths): 67 | """ 68 | Read all paths and overwrite the keys with each successive file. 69 | 70 | A custom config parser for passing JSON files. 71 | 72 | We use this instead of the standard ConfigParser as the JSON format 73 | can support list and dict types. 74 | 75 | This parser can also support comments using the following style "//" 76 | 77 | :type paths: list[str] 78 | :rtype: dict 79 | """ 80 | conf = {} 81 | 82 | for path in paths: 83 | lines = [] 84 | 85 | with open(path) as f: 86 | for line in f.readlines(): 87 | if not line.strip().startswith('//'): 88 | lines.append(line) 89 | 90 | data = '\n'.join(lines) 91 | if data: 92 | conf.update(json.loads(data)) 93 | 94 | return conf 95 | -------------------------------------------------------------------------------- /src/studiolibrary/config/default.json: -------------------------------------------------------------------------------- 1 | // This is the default config file and should NOT be changed. 2 | // 3 | // There are two ways to create a custom config. 4 | // 5 | // 1. You can create a "config.json" at repo/config/config.json. 6 | // This file will override any keys in this default.json file 7 | // and will be ignored by git. 8 | // 9 | // 2. The other way is to create an environment variable with the name 10 | // STUDIO_LIBRARY_CONFIG_PATH. The value of this variable should be the 11 | // full path to your config.json file. 12 | // 13 | // 3. Or you could use code to modify the config before loading the window. 14 | // import studiolibrary 15 | // studiolibrary.config.set("recursiveSearchDepth", 6) 16 | // studiolibrary.main() 17 | 18 | { 19 | // The database path is used for caching the library items. 20 | // You can use environment variables within the path. eg: {HOME} 21 | "databasePath": "{root}/.studiolibrary/database.json", 22 | 23 | // The temp location used for saving out items and thumbnails 24 | "tempPath": "{temp}/StudioLibrary/{user}", 25 | 26 | // The metadata path used for each item. Used for tags, item color etc 27 | // eg: /library/data/animation/nemo/.metadata 28 | "metadataPath": "{path}/.studiolibrary/metadata.json", 29 | 30 | // Used for saving persistent user data 31 | "settingsPath": "{local}/StudioLibrary/LibraryWidget.json", 32 | 33 | // The maximum walking depth from the root directory 34 | "recursiveSearchDepth": 5, 35 | 36 | // A list of paths to ignore when walking the root directory 37 | "ignorePaths": ["/."], 38 | 39 | // The command used to show a path in the file explorer 40 | //"showInFolderCmd": "konqueror \"{path}\"&", 41 | 42 | // Enables the scale factor option in the setting dialog 43 | // This might be useful when using high-DPI devices like a 4k monitor 44 | "scaleFactorEnabled": true, 45 | 46 | // Check if there are any new versions available on start up 47 | "checkForUpdatesEnabled": true, 48 | 49 | // A list of the default item plugins 50 | "itemRegistry": [ 51 | // This is an example item for development 52 | // "studiolibrarymaya.exampleitem.ExampleItem", 53 | // The maya file item is in development 54 | // "studiolibrarymaya.mayafileitem.MayaFileItem", 55 | "studiolibrarymaya.poseitem.PoseItem", 56 | "studiolibrarymaya.animitem.AnimItem", 57 | "studiolibrarymaya.mirroritem.MirrorItem", 58 | "studiolibrarymaya.setsitem.SetsItem", 59 | "studiolibrary.folderitem.FolderItem" 60 | ] 61 | } -------------------------------------------------------------------------------- /src/studiolibrary/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import studioqt 14 | import studiolibrary 15 | 16 | 17 | def main(*args, **kwargs): 18 | """ 19 | Convenience method for creating/showing a library widget instance. 20 | 21 | return studiolibrary.LibraryWindow.instance( 22 | name="", 23 | path="", 24 | show=True, 25 | lock=False, 26 | superusers=None, 27 | lockRegExp=None, 28 | unlockRegExp=None 29 | ) 30 | 31 | :rtype: studiolibrary.LibraryWindow 32 | """ 33 | # Reload all Studio Library modules when Shift is pressed. 34 | # This is for developers to test their changes in a DCC application. 35 | if studioqt.isShiftModifier(): 36 | import studiolibrary 37 | studiolibrary.reload() 38 | 39 | # Register all the items from the config file. 40 | import studiolibrary 41 | studiolibrary.registerItems() 42 | 43 | if studiolibrary.isMaya(): 44 | from studiolibrarymaya import mayalibrarywindow 45 | libraryWindow = mayalibrarywindow.MayaLibraryWindow.instance(*args, **kwargs) 46 | else: 47 | from studiolibrary import librarywindow 48 | libraryWindow = librarywindow.LibraryWindow.instance(*args, **kwargs) 49 | 50 | return libraryWindow 51 | 52 | 53 | if __name__ == "__main__": 54 | 55 | # Run the Studio Library in a QApplication instance 56 | import studioqt 57 | with studioqt.app(): 58 | studiolibrary.main() 59 | -------------------------------------------------------------------------------- /src/studiolibrary/resource.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | 15 | from studioqt import Icon 16 | from studioqt import Pixmap 17 | 18 | from . import utils 19 | 20 | 21 | PATH = os.path.abspath(__file__) 22 | DIRNAME = os.path.dirname(PATH) 23 | RESOURCE_DIRNAME = os.path.join(DIRNAME, "resource") 24 | 25 | 26 | def get(*args): 27 | """ 28 | This is a convenience function for returning the resource path. 29 | 30 | :rtype: str 31 | """ 32 | path = os.path.join(RESOURCE_DIRNAME, *args) 33 | return utils.normPath(path) 34 | 35 | 36 | def icon(*args, **kwargs): 37 | """ 38 | Return an Icon object from the given resource name. 39 | 40 | :rtype: str 41 | """ 42 | path = get("icons", *args) 43 | return Icon(pixmap(path, **kwargs)) 44 | 45 | 46 | def pixmap(name, scope="icons", extension="png", color=None): 47 | """ 48 | Return a Pixmap object from the given resource name. 49 | 50 | :type name: str 51 | :type scope: str 52 | :type extension: str 53 | :type color: str 54 | :rtype: QtWidgets.QPixmap 55 | """ 56 | if name.endswith(".svg"): 57 | extension = "" 58 | 59 | path = "" 60 | 61 | if os.path.exists(name): 62 | path = name 63 | 64 | elif extension: 65 | path = get(scope, name + "." + extension) 66 | if not os.path.exists(path): 67 | path = get(scope, name + ".svg") 68 | 69 | p = Pixmap(path) 70 | 71 | if color: 72 | p.setColor(color) 73 | 74 | return p 75 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-Light.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-SemiBold.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/fonts/OpenSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/fonts/OpenSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/add.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/add_28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/add_28.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/archive.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/arrows-rotate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/asset.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/assets.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/bars.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/blank.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/book.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/branch_closed_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/branch_closed_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/branch_closed_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/branch_closed_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/branch_open_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/branch_open_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/branch_open_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/branch_open_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/camera-alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/cancel.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/caret-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/caret-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/character.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/check_box_checked_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/check_box_checked_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/check_box_checked_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/check_box_checked_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/check_box_unchecked_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/check_box_unchecked_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/check_box_unchecked_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/check_box_unchecked_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/circle.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/cloud.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/cog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/cog.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/columns-3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/critical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/critical.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/cross.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/database.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/delete.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/drop_arrow_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/drop_arrow_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/drop_arrow_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/drop_arrow_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/environment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/error.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/expand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/eye-slash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/face.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/favorite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/file-alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder_collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/folder_collapsed.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder_expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/folder_expanded.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder_item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/folder_item.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/folder_open.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/folder_open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/groupby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/groupby.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/header.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/icon.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/image.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/inbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/info.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/layers.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/logo.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/logo_high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/logo_high.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/logo_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/magnifying-glass.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/plus-large.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/preview_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/preview_placeholder.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/question.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/radio_button_checked_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/radio_button_checked_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/radio_button_checked_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/radio_button_checked_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/radio_button_unchecked_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/radio_button_unchecked_black.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/radio_button_unchecked_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/radio_button_unchecked_white.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/search.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/settings.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/share.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/shot.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/sliders.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/sortby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/sortby.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/sync.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/tag.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/thumbnail.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/thumbnail_solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/thumbnail_solid.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/times.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/users.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/vehicle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/video.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/view_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/view_all.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/view_compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/view_compact.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/view_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/view_settings.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrary/resource/icons/warning.png -------------------------------------------------------------------------------- /src/studiolibrary/resource/icons/xmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/PlaceholderWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Frame 4 | 5 | 6 | 7 | 0 8 | 0 9 | 229 10 | 639 11 | 12 | 13 | 14 | Frame 15 | 16 | 17 | QFrame::NoFrame 18 | 19 | 20 | QFrame::Plain 21 | 22 | 23 | 0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 0 31 | 10 32 | 33 | 34 | 35 | QFrame::NoFrame 36 | 37 | 38 | QFrame::Plain 39 | 40 | 41 | 0 42 | 43 | 44 | 45 | 46 | 47 | 48 | 96 49 | 96 50 | 51 | 52 | 53 | 54 | 96 55 | 96 56 | 57 | 58 | 59 | 60 | 61 | 62 | ../resource/icons/preview_placeholder.png 63 | 64 | 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 2 78 | 79 | 80 | 81 | QFrame::NoFrame 82 | 83 | 84 | QFrame::Plain 85 | 86 | 87 | 0 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from .lightbox import Lightbox 14 | from .lineedit import LineEdit 15 | from .sortbymenu import SortByMenu 16 | from .groupbymenu import GroupByMenu 17 | from .filterbymenu import FilterByMenu 18 | from .messagebox import MessageBox, createMessageBox 19 | from .toastwidget import ToastWidget 20 | from .searchwidget import SearchWidget 21 | from .statuswidget import StatusWidget 22 | from .previewwidget import PreviewWidget 23 | from .menubarwidget import MenuBarWidget 24 | from .sidebarwidget import SidebarWidget 25 | from .groupboxwidget import GroupBoxWidget 26 | from .placeholderwidget import PlaceholderWidget 27 | from .itemswidget.item import Item 28 | from .itemswidget.groupitem import GroupItem 29 | from .itemswidget.itemswidget import ItemsWidget 30 | from .themesmenu import Theme, ThemesMenu 31 | from .librariesmenu import LibrariesMenu 32 | from .slideraction import SliderAction 33 | from .separatoraction import SeparatorAction 34 | from .iconpicker import IconPickerAction, IconPickerWidget 35 | from .colorpicker import ColorPickerAction, ColorPickerWidget 36 | from .sequencewidget import ImageSequenceWidget 37 | from .formwidget import FormWidget, FormDialog 38 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/groupboxwidget.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import logging 14 | 15 | from studiovendor.Qt import QtCore 16 | from studiovendor.Qt import QtWidgets 17 | 18 | from . import settings 19 | 20 | import studioqt 21 | import studiolibrary 22 | 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class GroupBoxWidget(QtWidgets.QFrame): 28 | 29 | toggled = QtCore.Signal(bool) 30 | 31 | def __init__(self, title, widget, persistent=False, *args, **kwargs): 32 | super(GroupBoxWidget, self).__init__(*args, **kwargs) 33 | 34 | self._widget = None 35 | self._persistent = None 36 | 37 | layout = QtWidgets.QVBoxLayout() 38 | layout.setContentsMargins(0, 0, 0, 0) 39 | layout.setSpacing(0) 40 | 41 | self.setLayout(layout) 42 | 43 | self._titleWidget = QtWidgets.QPushButton(self) 44 | self._titleWidget.setCheckable(True) 45 | self._titleWidget.setText(title) 46 | self._titleWidget.setObjectName("title") 47 | self._titleWidget.toggled.connect(self._toggled) 48 | 49 | on_path = studiolibrary.resource.get("icons", "caret-down.svg") 50 | off_path = studiolibrary.resource.get("icons", "caret-right.svg") 51 | icon = studioqt.Icon.fa(on_path, color="rgba(255,255,255,200)", off=off_path) 52 | self._titleWidget.setIcon(icon) 53 | 54 | self.layout().addWidget(self._titleWidget) 55 | 56 | self._widgetFrame = QtWidgets.QFrame(self) 57 | self._widgetFrame.setObjectName("frame") 58 | 59 | layout = QtWidgets.QVBoxLayout() 60 | layout.setContentsMargins(0, 0, 0, 0) 61 | layout.setSpacing(0) 62 | 63 | self._widgetFrame.setLayout(layout) 64 | 65 | self.layout().addWidget(self._widgetFrame) 66 | 67 | if widget: 68 | self.setWidget(widget) 69 | 70 | self.setPersistent(persistent) 71 | 72 | def setPersistent(self, persistent): 73 | """ 74 | Save and load the state of the widget to disk. 75 | 76 | :type persistent: bool 77 | """ 78 | self._persistent = persistent 79 | self.loadSettings() 80 | 81 | def title(self): 82 | """ 83 | Get the title for the group box. 84 | 85 | :rtype: str 86 | """ 87 | return self._titleWidget.text() 88 | 89 | def setWidget(self, widget): 90 | """ 91 | Set the widget to hide when the user clicks the title. 92 | 93 | :type widget: QWidgets.QWidget 94 | """ 95 | self._widget = widget 96 | # self._widget.setParent(self._widgetFrame) 97 | # self._widgetFrame.layout().addWidget(self._widget) 98 | 99 | def _toggled(self, visible): 100 | """ 101 | Triggered when the user clicks the title. 102 | 103 | :type visible: bool 104 | """ 105 | self.saveSettings() 106 | self.setChecked(visible) 107 | self.toggled.emit(visible) 108 | 109 | def isChecked(self): 110 | """ 111 | Check the checked state for the group box. 112 | 113 | :rtype: bool 114 | """ 115 | return self._titleWidget.isChecked() 116 | 117 | def setChecked(self, checked): 118 | """ 119 | Overriding this method to hide the widget when the state changes. 120 | 121 | :type checked: bool 122 | """ 123 | self._titleWidget.setChecked(checked) 124 | if self._widget: 125 | self._widget.setVisible(checked) 126 | 127 | def saveSettings(self): 128 | """Save the state to disc.""" 129 | if self._persistent: 130 | 131 | if not self.objectName(): 132 | raise NameError("No object name set for persistent widget.") 133 | 134 | data = {self.objectName(): {"checked": self.isChecked()}} 135 | settings.save(data) 136 | 137 | def loadSettings(self): 138 | """Load the state to disc.""" 139 | if self._persistent: 140 | 141 | if not self.objectName(): 142 | raise NameError("No object name set for persistent widget.") 143 | 144 | data = settings.read() 145 | data = data.get(self.objectName(), {}) 146 | 147 | if isinstance(data, dict): 148 | checked = data.get("checked", True) 149 | self.setChecked(checked) 150 | 151 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/groupbymenu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from functools import partial 14 | 15 | from studiovendor.Qt import QtGui 16 | from studiovendor.Qt import QtWidgets 17 | 18 | from .separatoraction import SeparatorAction 19 | 20 | 21 | class GroupByMenu(QtWidgets.QMenu): 22 | 23 | def __init__(self, name, parent, dataset): 24 | super(GroupByMenu, self).__init__(name, parent) 25 | 26 | self._dataset = dataset 27 | self.aboutToShow.connect(self.populateMenu) 28 | 29 | def setDataset(self, dataset): 30 | """ 31 | Set the dataset model for the menu: 32 | 33 | :type dataset: studiolibrary.Dataset 34 | """ 35 | self._dataset = dataset 36 | 37 | def dataset(self): 38 | """ 39 | Get the dataset model for the menu. 40 | 41 | :rtype: studiolibrary.Dataset 42 | """ 43 | return self._dataset 44 | 45 | def setGroupBy(self, groupName, groupOrder): 46 | """ 47 | Set the group by value for the dataset. 48 | 49 | :type groupName: str 50 | :type groupOrder: str 51 | """ 52 | if groupName: 53 | value = [groupName + ":" + groupOrder] 54 | else: 55 | value = None 56 | 57 | self.dataset().setGroupBy(value) 58 | self.dataset().search() 59 | 60 | def populateMenu(self): 61 | """ 62 | Show the menu options. 63 | """ 64 | self.clear() 65 | 66 | groupBy = self.dataset().groupBy() 67 | if groupBy: 68 | currentField = groupBy[0].split(":")[0] 69 | currentOrder = "dsc" if "dsc" in groupBy[0] else "asc" 70 | else: 71 | currentField = "" 72 | currentOrder = "" 73 | 74 | action = SeparatorAction("Group By", self) 75 | self.addAction(action) 76 | 77 | action = self.addAction("None") 78 | action.setCheckable(True) 79 | 80 | if not currentField: 81 | action.setChecked(True) 82 | 83 | callback = partial(self.setGroupBy, None, None) 84 | action.triggered.connect(callback) 85 | 86 | fields = self.dataset().fields() 87 | 88 | for field in fields: 89 | 90 | if not field.get("groupable"): 91 | continue 92 | 93 | name = field.get("name") 94 | 95 | action = self.addAction(name.title()) 96 | action.setCheckable(True) 97 | 98 | if currentField == name: 99 | action.setChecked(True) 100 | else: 101 | action.setChecked(False) 102 | 103 | callback = partial(self.setGroupBy, name, currentOrder) 104 | action.triggered.connect(callback) 105 | 106 | action = SeparatorAction("Group Order", self) 107 | self.addAction(action) 108 | 109 | action = self.addAction("Ascending") 110 | action.setCheckable(True) 111 | action.setChecked(currentOrder == "asc") 112 | 113 | callback = partial(self.setGroupBy, currentField, "asc") 114 | action.triggered.connect(callback) 115 | 116 | action = self.addAction("Descending") 117 | action.setCheckable(True) 118 | action.setChecked(currentOrder == "dsc") 119 | 120 | callback = partial(self.setGroupBy, currentField, "dsc") 121 | action.triggered.connect(callback) 122 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/itemswidget/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/itemswidget/itemdelegate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtWidgets 14 | 15 | from .groupitem import GroupItem 16 | 17 | 18 | class ItemDelegate(QtWidgets.QStyledItemDelegate): 19 | 20 | def __init__(self): 21 | """ 22 | This class is used to display data for the items in a ItemsWidget. 23 | """ 24 | QtWidgets.QStyledItemDelegate.__init__(self) 25 | 26 | self._itemsWidget = None 27 | 28 | def itemsWidget(self): 29 | """ 30 | Return the ItemsWidget that contains the item delegate. 31 | 32 | :rtype: studioqt.ItemsWidget 33 | """ 34 | return self._itemsWidget 35 | 36 | def setItemsWidget(self, itemsWidget): 37 | """ 38 | Set the ItemsWidget for the delegate. 39 | 40 | :type itemsWidget: studioqt.ItemsWidget 41 | :rtype: None 42 | """ 43 | self._itemsWidget = itemsWidget 44 | 45 | def sizeHint(self, option, index): 46 | """ 47 | Return the size for the given index. 48 | 49 | :type option: QtWidgets.QStyleOptionViewItem 50 | :type index: QtCore.QModelIndex 51 | :rtype: QtCore.QSize 52 | """ 53 | #This will be called for each row. 54 | item = self.itemsWidget().itemFromIndex(index) 55 | 56 | if isinstance(item, GroupItem): 57 | return item.sizeHint() 58 | 59 | return self.itemsWidget().itemSizeHint(index) 60 | 61 | def paint(self, painter, option, index): 62 | """ 63 | Paint performs low-level painting for the given model index. 64 | 65 | :type painter: QtWidgets.QPainter 66 | :type option: QtWidgets.QStyleOptionViewItem 67 | :type index: QtCore.QModelIndex 68 | :rtype: None 69 | """ 70 | item = self.itemsWidget().itemFromIndex(index) 71 | item.paint(painter, option, index) 72 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/librariesmenu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | """ 13 | Example: 14 | 15 | from PySide2 import QtGui 16 | import studiolibrary.widgets 17 | menu = studiolibrary.widgets.LibrariesMenu() 18 | point = QtGui.QCursor.pos() 19 | menu.exec_(point) 20 | """ 21 | from functools import partial 22 | 23 | from studiovendor.Qt import QtWidgets 24 | 25 | import studiolibrary 26 | 27 | 28 | class LibrariesMenu(QtWidgets.QMenu): 29 | 30 | def __init__(self, libraryWindow=None): 31 | super(LibrariesMenu, self).__init__(libraryWindow) 32 | 33 | self.setTitle('Libraries') 34 | 35 | libraries = studiolibrary.readSettings() 36 | default = studiolibrary.defaultLibrary() 37 | 38 | for name in libraries: 39 | 40 | library = libraries[name] 41 | 42 | path = library.get('path', '') 43 | kwargs = library.get('kwargs', {}) 44 | 45 | enabled = True 46 | if libraryWindow: 47 | enabled = name != libraryWindow.name() 48 | 49 | text = name 50 | if name == default and name.lower() != "default": 51 | text = name + " (default)" 52 | 53 | action = QtWidgets.QAction(text, self) 54 | action.setEnabled(enabled) 55 | callback = partial(self.showLibrary, name, path, **kwargs) 56 | action.triggered.connect(callback) 57 | self.addAction(action) 58 | 59 | def showLibrary(self, name, path, **kwargs): 60 | """ 61 | Show the library window which has given name and path. 62 | 63 | :type name: str 64 | :type path: str 65 | :type kwargs: dict 66 | """ 67 | studiolibrary.main(name, path, **kwargs) 68 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/placeholderwidget.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtWidgets 14 | 15 | import studioqt 16 | 17 | 18 | class PlaceholderWidget(QtWidgets.QWidget): 19 | 20 | def __init__(self, *args): 21 | QtWidgets.QWidget.__init__(self, *args) 22 | studioqt.loadUi(self) 23 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/separatoraction.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtWidgets 14 | 15 | 16 | __all__ = ["SeparatorAction"] 17 | 18 | 19 | class Line(QtWidgets.QFrame): 20 | pass 21 | 22 | 23 | class SeparatorWidgetAction(QtWidgets.QFrame): 24 | pass 25 | 26 | 27 | class SeparatorAction(QtWidgets.QWidgetAction): 28 | 29 | def __init__(self, label="", parent=None): 30 | """ 31 | :type parent: QtWidgets.QMenu 32 | """ 33 | QtWidgets.QWidgetAction.__init__(self, parent) 34 | 35 | self._widget = SeparatorWidgetAction(parent) 36 | 37 | self._label = QtWidgets.QLabel(self._widget) 38 | self._label.setText(label) 39 | 40 | self._line = Line(self._widget) 41 | self._line.setSizePolicy( 42 | QtWidgets.QSizePolicy.Expanding, 43 | QtWidgets.QSizePolicy.Expanding 44 | ) 45 | 46 | def setText(self, text): 47 | """ 48 | Set the text of the separator. 49 | 50 | :type text: str 51 | :rtype: None 52 | """ 53 | self.label().setText(text) 54 | 55 | def widget(self): 56 | """ 57 | Return the QFrame object. 58 | 59 | :rtype: Frame 60 | """ 61 | return self._widget 62 | 63 | def label(self): 64 | """ 65 | Return the QLabel object. 66 | 67 | :rtype: QtWidgets.QLabel 68 | """ 69 | return self._label 70 | 71 | def line(self): 72 | """ 73 | Return the line widget. 74 | 75 | :rtype: Line 76 | """ 77 | return self._line 78 | 79 | def createWidget(self, menu): 80 | """ 81 | This method is called by the QWidgetAction base class. 82 | 83 | :type menu: QtWidgets.QMenu 84 | """ 85 | actionWidget = self.widget() 86 | 87 | actionLayout = QtWidgets.QHBoxLayout() 88 | actionLayout.setContentsMargins(0, 0, 0, 0) 89 | actionLayout.addWidget(self.label()) 90 | actionLayout.addWidget(self.line()) 91 | actionWidget.setLayout(actionLayout) 92 | 93 | return actionWidget 94 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import studiolibrary 14 | 15 | _settings = None 16 | 17 | 18 | def path(): 19 | """ 20 | Get the settings path. 21 | 22 | :rtype: str 23 | """ 24 | return studiolibrary.localPath("widgets2.json") 25 | 26 | 27 | def get(key, default=None): 28 | """ 29 | Convenience function for getting the a value from disc. 30 | 31 | :type key: str 32 | :type default: object 33 | :rtype: object 34 | """ 35 | return read().get(key, default) 36 | 37 | 38 | def set(key, value): 39 | """ 40 | Convenience function for setting key values to disc. 41 | 42 | :type key: str 43 | :type value: object 44 | """ 45 | save({key: value}) 46 | 47 | 48 | def read(): 49 | """ 50 | Return the local settings from the location of the SETTING_PATH. 51 | 52 | :rtype: dict 53 | """ 54 | global _settings 55 | 56 | if not _settings: 57 | _settings = studiolibrary.readJson(path()) 58 | 59 | return _settings 60 | 61 | 62 | def save(data): 63 | """ 64 | Save the given dict to the local location of the SETTING_PATH. 65 | 66 | :type data: dict 67 | :rtype: None 68 | """ 69 | global _settings 70 | _settings = None 71 | studiolibrary.updateJson(path(), data) 72 | 73 | 74 | def reset(): 75 | """Remove and reset the item settings.""" 76 | global _settings 77 | _settings = None 78 | studiolibrary.removePath(path()) 79 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/sidebarwidget/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | from .sidebarwidget import SidebarWidget -------------------------------------------------------------------------------- /src/studiolibrary/widgets/slideraction.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtCore 14 | from studiovendor.Qt import QtWidgets 15 | 16 | 17 | __all__ = ["SliderAction"] 18 | 19 | 20 | class SliderWidgetAction(QtWidgets.QFrame): 21 | pass 22 | 23 | 24 | class SliderAction(QtWidgets.QWidgetAction): 25 | 26 | def __init__(self, label="", parent=None): 27 | """ 28 | :type parent: QtWidgets.QMenu 29 | """ 30 | QtWidgets.QWidgetAction.__init__(self, parent) 31 | 32 | self._widget = SliderWidgetAction(parent) 33 | self._label = QtWidgets.QLabel(label, self._widget) 34 | 35 | self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self._widget) 36 | self._slider.setSizePolicy( 37 | QtWidgets.QSizePolicy.Expanding, 38 | QtWidgets.QSizePolicy.Expanding 39 | ) 40 | 41 | self.valueChanged = self._slider.valueChanged 42 | 43 | def widget(self): 44 | """ 45 | Return the widget for this action. 46 | 47 | :rtype: QtWidgets.QWidget 48 | """ 49 | return self._widget 50 | 51 | def label(self): 52 | """ 53 | Return the QLabel object. 54 | 55 | :rtype: QtWidgets.QLabel 56 | """ 57 | return self._label 58 | 59 | def slider(self): 60 | """ 61 | Return the QLabel object. 62 | 63 | :rtype: QtWidgets.QSlider 64 | """ 65 | return self._slider 66 | 67 | def createWidget(self, menu): 68 | """ 69 | This method is called by the QWidgetAction base class. 70 | 71 | :type menu: QtWidgets.QMenu 72 | """ 73 | actionWidget = self.widget() 74 | 75 | actionLayout = QtWidgets.QHBoxLayout() 76 | actionLayout.setContentsMargins(0, 0, 0, 0) 77 | actionLayout.addWidget(self.label()) 78 | actionLayout.addWidget(self.slider()) 79 | actionWidget.setLayout(actionLayout) 80 | 81 | return actionWidget 82 | -------------------------------------------------------------------------------- /src/studiolibrary/widgets/sortbymenu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from functools import partial 14 | 15 | from studiovendor.Qt import QtGui 16 | from studiovendor.Qt import QtWidgets 17 | 18 | from .separatoraction import SeparatorAction 19 | 20 | 21 | class SortByMenu(QtWidgets.QMenu): 22 | 23 | def __init__(self, name, parent, dataset): 24 | super(SortByMenu, self).__init__(name, parent) 25 | 26 | self._dataset = dataset 27 | self.aboutToShow.connect(self.populateMenu) 28 | 29 | def setDataset(self, dataset): 30 | """ 31 | Set the dataset model for the menu: 32 | 33 | :type dataset: studiolibrary.Dataset 34 | """ 35 | self._dataset = dataset 36 | 37 | def dataset(self): 38 | """ 39 | Get the dataset model for the menu. 40 | 41 | :rtype: studiolibrary.Dataset 42 | """ 43 | return self._dataset 44 | 45 | def setSortBy(self, sortName, sortOrder): 46 | """ 47 | Set the sort by value for the dataset. 48 | 49 | :type sortName: str 50 | :type sortOrder: str 51 | """ 52 | if sortName == "Custom Order": 53 | sortOrder = "asc" 54 | 55 | value = sortName + ":" + sortOrder 56 | self.dataset().setSortBy([value]) 57 | self.dataset().search() 58 | 59 | def populateMenu(self): 60 | """ 61 | Show the menu options. 62 | """ 63 | self.clear() 64 | 65 | sortby = self.dataset().sortBy() 66 | if sortby: 67 | currentField = self.dataset().sortBy()[0].split(":")[0] 68 | currentOrder = "dsc" if "dsc" in self.dataset().sortBy()[0] else "asc" 69 | else: 70 | currentField = "" 71 | currentOrder = "" 72 | 73 | action = SeparatorAction("Sort By", self) 74 | self.addAction(action) 75 | 76 | fields = self.dataset().fields() 77 | 78 | for field in fields: 79 | 80 | if not field.get("sortable"): 81 | continue 82 | 83 | name = field.get("name") 84 | 85 | action = self.addAction(name.title()) 86 | action.setCheckable(True) 87 | 88 | if currentField == name: 89 | action.setChecked(True) 90 | else: 91 | action.setChecked(False) 92 | 93 | callback = partial(self.setSortBy, name, currentOrder) 94 | action.triggered.connect(callback) 95 | 96 | action = SeparatorAction("Sort Order", self) 97 | self.addAction(action) 98 | 99 | action = self.addAction("Ascending") 100 | action.setCheckable(True) 101 | action.setChecked(currentOrder == "asc") 102 | 103 | callback = partial(self.setSortBy, currentField, "asc") 104 | action.triggered.connect(callback) 105 | 106 | action = self.addAction("Descending") 107 | action.setCheckable(True) 108 | action.setChecked(currentOrder == "dsc") 109 | 110 | callback = partial(self.setSortBy, currentField, "dsc") 111 | action.triggered.connect(callback) 112 | -------------------------------------------------------------------------------- /src/studiolibrarymaya/README.md: -------------------------------------------------------------------------------- 1 | # Studio Library Items 2 | 3 | Items are used for loading and saving data. 4 | 5 | 6 | ### Pose Item 7 | 8 | Saving and loading a pose items 9 | 10 | ```python 11 | from studiolibrarymaya import poseitem 12 | 13 | path = "/AnimLibrary/Characters/Malcolm/malcolm.pose" 14 | objects = maya.cmds.ls(selection=True) or [] 15 | namespaces = [] 16 | 17 | # Saving a pose item 18 | poseitem.save(path, objects=objects) 19 | 20 | # Loading a pose item 21 | poseitem.load(path, objects=objects, namespaces=namespaces, key=True, mirror=False) 22 | ``` 23 | 24 | ### Animation Item 25 | 26 | Saving and loading animation items 27 | 28 | ```python 29 | from studiolibrarymaya import animitem 30 | 31 | path = "/AnimLibrary/Characters/Malcolm/malcolm.anim" 32 | objects = maya.cmds.ls(selection=True) or [] 33 | 34 | # Saving an animation item 35 | animitem.save(path, objects=objects, frameRange=(0, 200), bakeConnected=False) 36 | 37 | # Loading an animation item 38 | animitem.load(path, objects=objects, option="replace all", connect=False, currentTime=False) 39 | ``` 40 | 41 | Loading an animation to multiple namespaces 42 | 43 | ```python 44 | from studiolibrarymaya import animitem 45 | animitem.load(path, namespaces=["character1", "character2"], option="replace all") 46 | ``` 47 | 48 | ### Mirror Table Item 49 | 50 | Saving and loading mirror tables 51 | 52 | ```python 53 | from studiolibrarymaya import mirroritem 54 | 55 | path = "/AnimLibrary/Characters/Malcolm/malcolm.mirror" 56 | objects = maya.cmds.ls(selection=True) or [] 57 | 58 | # Saving a mirror table item 59 | mirroritem.save(path, objects=objects, leftSide="Lf", rightSide="Rf") 60 | 61 | # Loading a mirror table item 62 | mirroritem.load(path, objects=objects, namespaces=[], option="swap", animation=True, time=None) 63 | ``` 64 | 65 | ### Selection Set Item 66 | 67 | Saving and loading selection sets 68 | 69 | ```python 70 | from studiolibrarymaya import setsitem 71 | 72 | path = "/AnimLibrary/Characters/Malcolm/malcolm.set" 73 | objects = maya.cmds.ls(selection=True) or [] 74 | 75 | # Saving a selection sets item 76 | setsitem.save(path, objects=objects) 77 | 78 | # Loading a selection sets item 79 | setsitem.load(path, objects=objects, namespaces=[]) 80 | ``` 81 | 82 | 83 | ### Maya File Item (Development) 84 | 85 | Saving and loading a Maya file item 86 | 87 | This item can be used to load and save any Maya nodes. For example: 88 | locators and geometry. 89 | 90 | ```python 91 | from studiolibrarymaya import mayafileitem 92 | 93 | path = "/AnimLibrary/Characters/Malcolm/malcolm.mayafile" 94 | objects = maya.cmds.ls(selection=True) or [] 95 | 96 | # Saving the item to disc 97 | mayafileitem.save(path, objects=objects) 98 | 99 | # Loading the item from disc 100 | mayafileitem.load(path) 101 | ``` 102 | 103 | ### Example Item 104 | 105 | If you would like to create a custom item for saving and loading different data types, then please have a look at the [exampleitem.py](exampleitem.py) 106 | 107 | When developing a new item you can "Shift + Click" on the shelf icon which will reload all Studio Library modules including your changes to the item. 108 | 109 | Make sure you register any new items using either the "itemRegistry" key in the [config file](../studiolibrary/config/default.json) or by calling `studiolibrary.registerItem(cls)`. 110 | -------------------------------------------------------------------------------- /src/studiolibrarymaya/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | -------------------------------------------------------------------------------- /src/studiolibrarymaya/exampleitem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | """ 13 | NOTE: Make sure you register this item in the config. 14 | """ 15 | 16 | import os 17 | import logging 18 | 19 | from studiolibrarymaya import baseitem 20 | 21 | 22 | logger = logging.getLogger(__name__) 23 | 24 | 25 | class ExampleItem(baseitem.BaseItem): 26 | 27 | NAME = "Example" 28 | EXTENSION = ".example" 29 | ICON_PATH = os.path.join(os.path.dirname(__file__), "icons", "pose.png") 30 | 31 | def loadSchema(self, **kwargs): 32 | """ 33 | Get the schema used for loading the example item. 34 | 35 | :rtype: list[dict] 36 | """ 37 | return [ 38 | { 39 | "name": "option", 40 | "type": "bool", 41 | "default": False, 42 | "persistent": True, 43 | }, 44 | ] 45 | 46 | def load(self, **kwargs): 47 | """ 48 | The load method is called with the user values from the load schema. 49 | 50 | :type kwargs: dict 51 | """ 52 | logger.info("Loading %s %s", self.path(), kwargs) 53 | raise NotImplementedError("The load method is not implemented!") 54 | 55 | def saveSchema(self, **kwargs): 56 | """ 57 | Get the schema used for saving the example item. 58 | 59 | :rtype: list[dict] 60 | """ 61 | return [ 62 | # The 'name' field and the 'folder' field are both required by 63 | # the BaseItem. How this is handled may change in the future. 64 | { 65 | "name": "folder", 66 | "type": "path", 67 | "layout": "vertical", 68 | "visible": False, 69 | }, 70 | { 71 | "name": "name", 72 | "type": "string", 73 | "layout": "vertical" 74 | }, 75 | { 76 | "name": "fileType", 77 | "type": "enum", 78 | "layout": "vertical", 79 | "default": "mayaAscii", 80 | "items": ["mayaAscii", "mayaBinary"], 81 | "persistent": True 82 | }, 83 | ] 84 | 85 | def save(self, **kwargs): 86 | """ 87 | The save method is called with the user values from the save schema. 88 | 89 | :type kwargs: dict 90 | """ 91 | logger.info("Saving %s %s", self.path(), kwargs) 92 | raise NotImplementedError("The save method is not implemented!") 93 | -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/animation.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/arrow.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/file.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/insert.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/insertConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/insertConnect.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/merge.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/mergeConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/mergeConnect.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/mirrortable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/mirrortable.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/pose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/pose.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/replace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/replace.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/replaceCompletely.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/replaceCompletely.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/replaceConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/replaceConnect.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/selectionSet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/selectionSet.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/icons/selectionSet2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiolibrarymaya/icons/selectionSet2.png -------------------------------------------------------------------------------- /src/studiolibrarymaya/mayafileitem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | """ 13 | NOTE: Make sure you register this item in the config. 14 | """ 15 | 16 | import os 17 | import logging 18 | 19 | import maya.cmds 20 | 21 | from studiolibrarymaya import baseitem 22 | 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class MayaFileItem(baseitem.BaseItem): 28 | 29 | NAME = "Maya File" 30 | TYPE = NAME 31 | EXTENSION = ".mayafile" 32 | ICON_PATH = os.path.join(os.path.dirname(__file__), "icons", "file.png") 33 | 34 | def transferPath(self): 35 | return self.path() + "/mayafile.ma" 36 | 37 | def loadSchema(self, **kwargs): 38 | """ 39 | Get the schema used for loading the example item. 40 | 41 | :rtype: list[dict] 42 | """ 43 | return [] 44 | 45 | def load(self, **kwargs): 46 | """ 47 | The load method is called with the user values from the load schema. 48 | 49 | :type kwargs: dict 50 | """ 51 | logger.info("Loading %s %s", self.path(), kwargs) 52 | 53 | maya.cmds.file( 54 | self.transferPath(), 55 | i=True, 56 | type="mayaAscii", 57 | options="v=0;", 58 | preserveReferences=True, 59 | mergeNamespacesOnClash=False, 60 | ) 61 | 62 | def saveSchema(self, **kwargs): 63 | """ 64 | Get the schema used for saving the example item. 65 | 66 | :rtype: list[dict] 67 | """ 68 | return [ 69 | # The 'name' field and the 'folder' field are both required by 70 | # the BaseItem. How this is handled may change in the future. 71 | { 72 | "name": "folder", 73 | "type": "path", 74 | "layout": "vertical", 75 | "visible": False, 76 | }, 77 | { 78 | "name": "name", 79 | "type": "string", 80 | "layout": "vertical" 81 | }, 82 | { 83 | "name": "objects", 84 | "type": "objects", 85 | "layout": "vertical" 86 | }, 87 | ] 88 | 89 | def save(self, **kwargs): 90 | """ 91 | The save method is called with the user values from the save schema. 92 | 93 | :type kwargs: dict 94 | """ 95 | logger.info("Saving %s %s", self.path(), kwargs) 96 | 97 | super(MayaFileItem, self).save(**kwargs) 98 | 99 | maya.cmds.file( 100 | self.transferPath(), 101 | type="mayaAscii", 102 | options="v=0;", 103 | preserveReferences=True, 104 | exportSelected=True 105 | ) 106 | -------------------------------------------------------------------------------- /src/studiolibrarymaya/setsitem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | 15 | try: 16 | import mutils 17 | except ImportError as error: 18 | print(error) 19 | 20 | from studiolibrarymaya import baseitem 21 | 22 | 23 | def save(path, *args, **kwargs): 24 | """Convenience function for saving a SetsItem.""" 25 | SetsItem(path).safeSave(*args, **kwargs) 26 | 27 | 28 | def load(path, *args, **kwargs): 29 | """Convenience function for loading a SetsItem.""" 30 | SetsItem(path).load(*args, **kwargs) 31 | 32 | 33 | class SetsItem(baseitem.BaseItem): 34 | 35 | NAME = "Selection Set" 36 | EXTENSION = ".set" 37 | ICON_PATH = os.path.join(os.path.dirname(__file__), "icons", "selectionSet.png") 38 | TRANSFER_CLASS = mutils.SelectionSet 39 | TRANSFER_BASENAME = "set.json" 40 | 41 | def loadFromCurrentValues(self): 42 | """Load the selection set using the settings for this item.""" 43 | self.load(namespaces=self.namespaces()) 44 | 45 | def load(self, namespaces=None): 46 | """ 47 | :type namespaces: list[str] | None 48 | """ 49 | self.selectContent(namespaces=namespaces) 50 | 51 | def save(self, objects, **kwargs): 52 | """ 53 | Save all the given object data to the item path on disc. 54 | 55 | :type objects: list[str] 56 | :type kwargs: dict 57 | """ 58 | super(SetsItem, self).save(**kwargs) 59 | 60 | # Save the selection set to the given path 61 | mutils.saveSelectionSet( 62 | self.path() + "/set.json", 63 | objects, 64 | metadata={"description": kwargs.get("comment", "")} 65 | ) 66 | -------------------------------------------------------------------------------- /src/studioqt/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studioqt.utils import * 14 | from studioqt.icon import Icon 15 | from studioqt.menu import Menu 16 | from studioqt.color import Color 17 | from studioqt.pixmap import Pixmap 18 | from studioqt.stylesheet import StyleSheet 19 | from studioqt.decorators import showWaitCursor 20 | from studioqt.decorators import showArrowCursor 21 | from studioqt.imagesequence import ImageSequence 22 | -------------------------------------------------------------------------------- /src/studioqt/color.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtGui 14 | 15 | 16 | COLORS = [ 17 | {"name": "red", "color": "rgb(255, 115, 100)"}, 18 | {"name": "orange", "color": "rgb(255, 150, 100)"}, 19 | {"name": "yellow", "color": "rgb(255, 210, 103)"}, 20 | {"name": "green", "color": "rgb(140, 220, 140)"}, 21 | {"name": "blue", "color": "rgb(110, 175, 255)"}, 22 | {"name": "purple", "color": "rgb(160, 120, 255)"}, 23 | {"name": "pink", "color": "rgb(230, 130, 180)"}, 24 | {"name": "grey", "color": "rgb(125, 125, 140)"}, 25 | ] 26 | 27 | COLORS_INDEXED = {c["name"]: c for c in COLORS} 28 | 29 | 30 | class Color(QtGui.QColor): 31 | 32 | @classmethod 33 | def fromColor(cls, color): 34 | """ 35 | :type color: QtGui.QColor 36 | """ 37 | color = ('rgba(%d, %d, %d, %d)' % color.getRgb()) 38 | return cls.fromString(color) 39 | 40 | @classmethod 41 | def fromString(cls, c): 42 | """ 43 | :type c: str 44 | """ 45 | a = 255 46 | 47 | c = c.replace(";", "") 48 | if not c.startswith("rgb"): 49 | c = COLORS_INDEXED.get(c) 50 | if c: 51 | c = c.get("color") 52 | else: 53 | return cls(0, 0, 0, 255) 54 | 55 | try: 56 | r, g, b, a = c.replace("rgb(", "").replace("rgba(", "").replace(")", "").split(",") 57 | except ValueError: 58 | r, g, b = c.replace("rgb(", "").replace(")", "").split(",") 59 | 60 | return cls(int(r), int(g), int(b), int(a)) 61 | 62 | def __eq__(self, other): 63 | if isinstance(other, Color): 64 | return self.toString() == other.toString() 65 | else: 66 | return QtGui.QColor.__eq__(self, other) 67 | 68 | def toString(self): 69 | """ 70 | :type: str 71 | """ 72 | return 'rgba(%d, %d, %d, %d)' % self.getRgb() 73 | 74 | def isDark(self): 75 | """ 76 | :type: bool 77 | """ 78 | return self.red() < 125 and self.green() < 125 and self.blue() < 125 79 | -------------------------------------------------------------------------------- /src/studioqt/decorators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor.Qt import QtGui 14 | from studiovendor.Qt import QtCore 15 | from studiovendor.Qt import QtWidgets 16 | 17 | 18 | def showWaitCursor(fn): 19 | 20 | def wrapped(*args, **kwargs): 21 | cursor = QtGui.QCursor(QtCore.Qt.WaitCursor) 22 | QtWidgets.QApplication.setOverrideCursor(cursor) 23 | try: 24 | return fn(*args, **kwargs) 25 | finally: 26 | QtWidgets.QApplication.restoreOverrideCursor() 27 | 28 | wrapped.__name__ = fn.__name__ 29 | wrapped.__doc__ = fn.__doc__ 30 | 31 | return wrapped 32 | 33 | 34 | def showArrowCursor(fn): 35 | 36 | def wrapped(*args, **kwargs): 37 | cursor = QtGui.QCursor(QtCore.Qt.ArrowCursor) 38 | QtWidgets.QApplication.setOverrideCursor(cursor) 39 | try: 40 | return fn(*args, **kwargs) 41 | finally: 42 | QtWidgets.QApplication.restoreOverrideCursor() 43 | 44 | wrapped.__name__ = fn.__name__ 45 | wrapped.__doc__ = fn.__doc__ 46 | 47 | return wrapped 48 | -------------------------------------------------------------------------------- /src/studioqt/menu.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor import six 14 | from studiovendor.Qt import QtWidgets 15 | 16 | 17 | class Menu(QtWidgets.QMenu): 18 | 19 | def __init__(self, *args): 20 | QtWidgets.QMenu.__init__(self, *args) 21 | 22 | def findAction(self, text): 23 | """ 24 | Return the action that contains the given text. 25 | 26 | :type text: str 27 | 28 | :rtype: QtWidgets.QAction 29 | """ 30 | for child in self.children(): 31 | 32 | action = None 33 | 34 | if isinstance(child, QtWidgets.QMenu): 35 | action = child.menuAction() 36 | elif isinstance(child, QtWidgets.QAction): 37 | action = child 38 | 39 | if action and action.text().lower() == text.lower(): 40 | return action 41 | 42 | def insertAction(self, before, *args): 43 | """ 44 | Add support for finding the before action by the given string. 45 | 46 | :type before: str or QtWidget.QAction 47 | :type args: list 48 | 49 | :rtype: QtWidgets.QAction 50 | """ 51 | if isinstance(before, six.string_types): 52 | before = self.findAction(before) 53 | 54 | return QtWidgets.QMenu.insertAction(self, before, *args) 55 | 56 | def insertMenu(self, before, menu): 57 | """ 58 | Add support for finding the before action by the given string. 59 | 60 | :type before: str or QtWidget.QAction 61 | :type menu: QtWidgets.QMenu 62 | 63 | :rtype: QtWidgets.QAction 64 | """ 65 | if isinstance(before, six.string_types): 66 | before = self.findAction(before) 67 | 68 | QtWidgets.QMenu.insertMenu(self, before, menu) 69 | 70 | def insertSeparator(self, before): 71 | """ 72 | Add support for finding the before action by the given string. 73 | 74 | :type before: str or QtWidget.QAction 75 | 76 | :rtype: QtWidgets.QAction 77 | """ 78 | if isinstance(before, six.string_types): 79 | before = self.findAction(before) 80 | 81 | return QtWidgets.QMenu.insertSeparator(self, before) 82 | -------------------------------------------------------------------------------- /src/studioqt/pixmap.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | from studiovendor import six 14 | from studiovendor.Qt import QtGui 15 | 16 | import studioqt 17 | 18 | 19 | class Pixmap(QtGui.QPixmap): 20 | 21 | def __init__(self, *args): 22 | QtGui.QPixmap.__init__(self, *args) 23 | 24 | self._color = None 25 | 26 | def setColor(self, color): 27 | """ 28 | :type color: QtGui.QColor 29 | :rtype: None 30 | """ 31 | if isinstance(color, six.string_types): 32 | color = studioqt.Color.fromString(color) 33 | 34 | if not self.isNull(): 35 | painter = QtGui.QPainter(self) 36 | painter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceIn) 37 | painter.setBrush(color) 38 | painter.setPen(color) 39 | painter.drawRect(self.rect()) 40 | painter.end() 41 | -------------------------------------------------------------------------------- /src/studioqt/stylesheet.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 by Kurt Rathjen. All Rights Reserved. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. This library is distributed in the 7 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 8 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | # See the GNU Lesser General Public License for more details. 10 | # You should have received a copy of the GNU Lesser General Public 11 | # License along with this library. If not, see . 12 | 13 | import os 14 | import re 15 | 16 | import studioqt 17 | 18 | 19 | class StyleSheet(object): 20 | 21 | @classmethod 22 | def fromPath(cls, path, **kwargs): 23 | """ 24 | :type path: str 25 | :rtype: str 26 | """ 27 | styleSheet = cls() 28 | data = styleSheet.read(path) 29 | data = StyleSheet.format(data, **kwargs) 30 | styleSheet.setData(data) 31 | return styleSheet 32 | 33 | @classmethod 34 | def fromText(cls, text, options=None): 35 | """ 36 | :type text: str 37 | :rtype: str 38 | """ 39 | styleSheet = cls() 40 | data = StyleSheet.format(text, options=options) 41 | styleSheet.setData(data) 42 | return styleSheet 43 | 44 | def __init__(self): 45 | self._data = "" 46 | 47 | def setData(self, data): 48 | """ 49 | :type data: str 50 | """ 51 | self._data = data 52 | 53 | def data(self): 54 | """ 55 | :rtype: str 56 | """ 57 | return self._data 58 | 59 | @staticmethod 60 | def read(path): 61 | """ 62 | :type path: str 63 | :rtype: str 64 | """ 65 | data = "" 66 | 67 | if os.path.isfile(path): 68 | with open(path, "r") as f: 69 | data = f.read() 70 | 71 | return data 72 | 73 | @staticmethod 74 | def format(data=None, options=None, dpi=1): 75 | """ 76 | :type data: 77 | :type options: dict 78 | :rtype: str 79 | """ 80 | if options is not None: 81 | keys = list(options.keys()) 82 | keys.sort(key=len, reverse=True) 83 | for key in keys: 84 | data = data.replace(key, options[key]) 85 | 86 | reDpi = re.compile("[0-9]+px") 87 | newData = [] 88 | 89 | for line in data.split("\n"): 90 | dpi_ = reDpi.search(line) 91 | 92 | if dpi_: 93 | new = dpi_.group().replace("px", "") 94 | val = int(new) 95 | if val > 0: 96 | val = int(val * dpi) 97 | line = line.replace(dpi_.group(), str(val) + "px") 98 | 99 | newData.append(line) 100 | 101 | data = "\n".join(newData) 102 | return data 103 | -------------------------------------------------------------------------------- /src/studiovendor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krathjen/studiolibrary/0a2df600ab7b5353a995af79636751bdc6d2914c/src/studiovendor/__init__.py --------------------------------------------------------------------------------