├── tests ├── __init__.py ├── test_launch.py ├── test_docks.py ├── test_profiles.py └── util.py ├── allzpark ├── __init__.py ├── vendor │ ├── __init__.py │ ├── six-1.12.0.dist-info │ │ ├── INSTALLER │ │ ├── top_level.txt │ │ ├── WHEEL │ │ ├── RECORD │ │ ├── LICENSE │ │ └── METADATA │ ├── transitions-0.6.9.dist-info │ │ ├── INSTALLER │ │ ├── top_level.txt │ │ ├── WHEEL │ │ ├── LICENSE │ │ └── RECORD │ ├── transitions │ │ ├── version.py │ │ ├── __init__.py │ │ └── extensions │ │ │ ├── __init__.py │ │ │ ├── factory.py │ │ │ ├── locking.py │ │ │ └── states.py │ ├── LICENSE │ └── QtImageViewer.py ├── resources │ ├── Logo_64.png │ ├── Logo_128.png │ ├── up_arrow.png │ ├── App_Heidi_32.png │ ├── App_Pulse_32.png │ ├── branch_open.png │ ├── down_arrow.png │ ├── up_arrow_dim.png │ ├── Alert_Info_32.png │ ├── File_Query_32.png │ ├── branch_closed.png │ ├── branch_open-on.png │ ├── down_arrow_dim.png │ ├── Action_GoHome_32.png │ ├── Action_Stop_1_32.png │ ├── Alert_Warning_32.png │ ├── App_Generic_4_32.png │ ├── Default_Profile.png │ ├── File_Archive_32.png │ ├── Prefs_Screen_32.png │ ├── branch_closed-on.png │ ├── checkbox_checked.png │ ├── up_arrow_disabled.png │ ├── Action_GoDown_3_32.png │ ├── branch_open-on-dim.png │ ├── checkbox_unchecked.png │ ├── down_arrow_disabled.png │ ├── Action_GoUp_3_Large_32.png │ ├── branch_closed-on-dim.png │ ├── checkbox_checked_dim.png │ ├── checkbox_unchecked_dim.png │ ├── checkbox_checked_bright.png │ ├── checkbox_unchecked_bright.png │ ├── fonts │ │ ├── opensans │ │ │ ├── OpenSans-Bold.ttf │ │ │ ├── OpenSans-Light.ttf │ │ │ ├── OpenSans-Italic.ttf │ │ │ ├── OpenSans-Regular.ttf │ │ │ ├── OpenSans-Semibold.ttf │ │ │ ├── OpenSans-BoldItalic.ttf │ │ │ ├── OpenSans-ExtraBold.ttf │ │ │ ├── OpenSans-LightItalic.ttf │ │ │ ├── OpenSans-ExtraBoldItalic.ttf │ │ │ └── OpenSans-SemiboldItalic.ttf │ │ └── jetbrainsmono │ │ │ ├── JetBrainsMono-Bold.ttf │ │ │ ├── JetBrainsMono-Light.ttf │ │ │ ├── JetBrainsMono-Italic.ttf │ │ │ ├── JetBrainsMono-Medium.ttf │ │ │ ├── JetBrainsMono-Regular.ttf │ │ │ ├── JetBrainsMono-ExtraBold.ttf │ │ │ ├── JetBrainsMono-ExtraLight.ttf │ │ │ ├── JetBrainsMono-SemiLight.ttf │ │ │ ├── JetBrainsMono-Bold-Italic.ttf │ │ │ ├── JetBrainsMono-Light-Italic.ttf │ │ │ ├── JetBrainsMono-Medium-Italic.ttf │ │ │ ├── JetBrainsMono-ExtraBold-Italic.ttf │ │ │ ├── JetBrainsMono-ExtraLight-Italic.ttf │ │ │ └── JetBrainsMono-SemiLight-Italic.ttf │ ├── exclamation-circle-fill.svg │ ├── check-circle-fill.svg │ ├── chat-square-dots-fill.svg │ ├── profile_current.svg │ ├── profile_normal.svg │ ├── star_bright.svg │ ├── collapse.svg │ ├── star_dim.svg │ ├── filter_on.svg │ ├── expand.svg │ ├── gear-fill.svg │ ├── profile_current_star.svg │ ├── profile_normal_star.svg │ ├── version_on.svg │ ├── version_off.svg │ ├── refresh.svg │ └── filter_off.svg ├── __main__.py ├── version.py ├── delegates.py ├── _rezapi.py ├── allzparkconfig.py ├── util.py └── resources.py ├── docs ├── pages │ ├── CNAME │ ├── localisation.md │ ├── workflow.md │ ├── quickstart.md │ ├── gui.md │ ├── getting-advanced.md │ ├── windows.md │ ├── contributing.md │ ├── about.md │ ├── avalon.md │ ├── rez.md │ └── index.md ├── deploy.ps1 ├── serve.ps1 ├── requirements.txt ├── theme │ ├── logo.png │ ├── landing.png │ ├── logo32.png │ ├── allspark.png │ ├── Peinture Fraiche.ttf │ ├── 404.html │ ├── main.html │ ├── rain.css │ ├── landing.html │ ├── extra.js │ ├── rain.js │ ├── landing.css │ └── extra.css ├── install.ps1 └── mkdocs.yml ├── launch.ps1 ├── env.ps1 ├── setup.cfg ├── .gitignore ├── setup.py ├── README.md ├── azure-pipelines.yml └── LICENCE.txt /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /allzpark/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /allzpark/vendor/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/pages/CNAME: -------------------------------------------------------------------------------- 1 | allzpark.com -------------------------------------------------------------------------------- /docs/pages/localisation.md: -------------------------------------------------------------------------------- 1 | Shared packages -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | six 2 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions-0.6.9.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions-0.6.9.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | transitions 2 | -------------------------------------------------------------------------------- /launch.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/launch.ps1 -------------------------------------------------------------------------------- /env.ps1: -------------------------------------------------------------------------------- 1 | rez env --quiet bleeding_rez git graphviz localz python-3 pyside2 $args -------------------------------------------------------------------------------- /docs/deploy.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/deploy.ps1 -------------------------------------------------------------------------------- /docs/serve.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/serve.ps1 -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material==4.4.0 2 | mkdocs-git-revision-date-plugin==0.1.5 3 | -------------------------------------------------------------------------------- /docs/theme/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/theme/logo.png -------------------------------------------------------------------------------- /docs/theme/landing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/theme/landing.png -------------------------------------------------------------------------------- /docs/theme/logo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/theme/logo32.png -------------------------------------------------------------------------------- /docs/theme/allspark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/theme/allspark.png -------------------------------------------------------------------------------- /allzpark/resources/Logo_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Logo_64.png -------------------------------------------------------------------------------- /allzpark/resources/Logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Logo_128.png -------------------------------------------------------------------------------- /allzpark/resources/up_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/up_arrow.png -------------------------------------------------------------------------------- /docs/theme/Peinture Fraiche.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/docs/theme/Peinture Fraiche.ttf -------------------------------------------------------------------------------- /allzpark/resources/App_Heidi_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/App_Heidi_32.png -------------------------------------------------------------------------------- /allzpark/resources/App_Pulse_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/App_Pulse_32.png -------------------------------------------------------------------------------- /allzpark/resources/branch_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_open.png -------------------------------------------------------------------------------- /allzpark/resources/down_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/down_arrow.png -------------------------------------------------------------------------------- /allzpark/resources/up_arrow_dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/up_arrow_dim.png -------------------------------------------------------------------------------- /allzpark/resources/Alert_Info_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Alert_Info_32.png -------------------------------------------------------------------------------- /allzpark/resources/File_Query_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/File_Query_32.png -------------------------------------------------------------------------------- /allzpark/resources/branch_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_closed.png -------------------------------------------------------------------------------- /allzpark/resources/branch_open-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_open-on.png -------------------------------------------------------------------------------- /allzpark/resources/down_arrow_dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/down_arrow_dim.png -------------------------------------------------------------------------------- /allzpark/resources/Action_GoHome_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Action_GoHome_32.png -------------------------------------------------------------------------------- /allzpark/resources/Action_Stop_1_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Action_Stop_1_32.png -------------------------------------------------------------------------------- /allzpark/resources/Alert_Warning_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Alert_Warning_32.png -------------------------------------------------------------------------------- /allzpark/resources/App_Generic_4_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/App_Generic_4_32.png -------------------------------------------------------------------------------- /allzpark/resources/Default_Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Default_Profile.png -------------------------------------------------------------------------------- /allzpark/resources/File_Archive_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/File_Archive_32.png -------------------------------------------------------------------------------- /allzpark/resources/Prefs_Screen_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Prefs_Screen_32.png -------------------------------------------------------------------------------- /allzpark/resources/branch_closed-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_closed-on.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_checked.png -------------------------------------------------------------------------------- /allzpark/resources/up_arrow_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/up_arrow_disabled.png -------------------------------------------------------------------------------- /allzpark/resources/Action_GoDown_3_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Action_GoDown_3_32.png -------------------------------------------------------------------------------- /allzpark/resources/branch_open-on-dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_open-on-dim.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_unchecked.png -------------------------------------------------------------------------------- /allzpark/resources/down_arrow_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/down_arrow_disabled.png -------------------------------------------------------------------------------- /docs/install.ps1: -------------------------------------------------------------------------------- 1 | # Install requirements with pipz 2 | # Usage: . ./install.ps1 3 | rez env pipz -- install --requirement ./requirements.txt -------------------------------------------------------------------------------- /docs/theme/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | Page no found. :( 5 | {{ super() }} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /allzpark/resources/Action_GoUp_3_Large_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/Action_GoUp_3_Large_32.png -------------------------------------------------------------------------------- /allzpark/resources/branch_closed-on-dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/branch_closed-on-dim.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_checked_dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_checked_dim.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_unchecked_dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_unchecked_dim.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_checked_bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_checked_bright.png -------------------------------------------------------------------------------- /allzpark/resources/checkbox_unchecked_bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/checkbox_unchecked_bright.png -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-Light.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-Semibold.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Bold.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Light.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/opensans/OpenSans-SemiboldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/opensans/OpenSans-SemiboldItalic.ttf -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.32.3) 3 | Root-Is-Purelib: true 4 | Tag: py2-none-any 5 | Tag: py3-none-any 6 | 7 | -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Medium.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Regular.ttf -------------------------------------------------------------------------------- /allzpark/vendor/transitions-0.6.9.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.32.2) 3 | Root-Is-Purelib: true 4 | Tag: py2-none-any 5 | Tag: py3-none-any 6 | 7 | -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraBold.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraLight.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-SemiLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-SemiLight.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Bold-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Bold-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Light-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Light-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Medium-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-Medium-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraBold-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraBold-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraLight-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-ExtraLight-Italic.ttf -------------------------------------------------------------------------------- /allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-SemiLight-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mottosso/allzpark/HEAD/allzpark/resources/fonts/jetbrainsmono/JetBrainsMono-SemiLight-Italic.ttf -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_files = LICENSE.txt 3 | 4 | [bdist_wheel] 5 | # Support for both Python 2 and 3 6 | universal=1 7 | 8 | [nosetests] 9 | verbosity=3 10 | detailed-errors=1 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | __pycache__ 4 | __version__.py 5 | build 6 | dist 7 | 8 | # Development 9 | .vscode 10 | .idea 11 | 12 | site 13 | 14 | *-stdout.txt 15 | *-stderr.txt 16 | *-stdin.txt -------------------------------------------------------------------------------- /allzpark/vendor/transitions/version.py: -------------------------------------------------------------------------------- 1 | """ Contains the current version of transition which is used in setup.py and can also be used 2 | to determine transitions's version during runtime. 3 | """ 4 | 5 | __version__ = '0.6.9' 6 | -------------------------------------------------------------------------------- /docs/theme/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block site_meta %} 4 | {{ super() }} 5 | 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
Updated {{ page.meta.revision_date }}
10 | {{ super() }} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /allzpark/resources/exclamation-circle-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /allzpark/resources/check-circle-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /allzpark/resources/chat-square-dots-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /allzpark/resources/profile_current.svg: -------------------------------------------------------------------------------- 1 | profile_current -------------------------------------------------------------------------------- /allzpark/resources/profile_normal.svg: -------------------------------------------------------------------------------- 1 | profile_normal -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | six-1.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | six-1.12.0.dist-info/LICENSE,sha256=5zL1TaWPPpzwxI6LUSlIk2_Pc2G9WK-mOpo8OSv3lK0,1066 3 | six-1.12.0.dist-info/METADATA,sha256=CRdYkKPKCFJr7-qA8PDpBklGXfXJ3xu4mu5tkLBDL04,1940 4 | six-1.12.0.dist-info/RECORD,, 5 | six-1.12.0.dist-info/WHEEL,sha256=_wJFdOYk7i3xxT8ElOkUJvOdOvfNGbR9g-bf6UQT6sU,110 6 | six-1.12.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 7 | six.py,sha256=h9jch2pS86y4R36pKRS3LOYUCVFNIJMRwjZ4fJDtJ44,32452 8 | six.pyc,, 9 | -------------------------------------------------------------------------------- /allzpark/__main__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | # Debugging, measure start-up time 5 | # NOTE: Handle this prior to importing anything 6 | if os.getenv("ALLZPARK_STARTTIME"): 7 | 8 | try: 9 | t0 = float(os.getenv("ALLZPARK_STARTTIME")) 10 | t1 = time.time() 11 | 12 | except ValueError: 13 | raise ValueError( 14 | "ALLZPARK_STARTTIME must be in format time.time()" 15 | ) 16 | 17 | duration = t1 - t0 18 | print("shell to python: %.2f s" % duration) 19 | 20 | 21 | import sys 22 | from . import cli 23 | sys.exit(cli.main()) 24 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | transitions 3 | ----------- 4 | 5 | A lightweight, object-oriented state machine implementation in Python. Compatible with Python 2.7+ and 3.0+. 6 | """ 7 | 8 | from __future__ import absolute_import 9 | from .version import __version__ 10 | from .core import (State, Transition, Event, EventData, Machine, MachineError) 11 | 12 | __copyright__ = "Copyright (c) 2017 Tal Yarkoni" 13 | __license__ = "MIT" 14 | __summary__ = "A lightweight, object-oriented finite state machine in Python" 15 | __uri__ = "https://github.com/tyarkoni/transitions" 16 | -------------------------------------------------------------------------------- /allzpark/resources/star_bright.svg: -------------------------------------------------------------------------------- 1 | star_bright -------------------------------------------------------------------------------- /allzpark/resources/collapse.svg: -------------------------------------------------------------------------------- 1 | collapse 2 | -------------------------------------------------------------------------------- /allzpark/resources/star_dim.svg: -------------------------------------------------------------------------------- 1 | star_dim 2 | -------------------------------------------------------------------------------- /allzpark/resources/filter_on.svg: -------------------------------------------------------------------------------- 1 | filter_on 2 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions/extensions/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | transitions.extensions 3 | ---------------------- 4 | 5 | Additional functionality such as hierarchical (nested) machine support, Graphviz-based diagram creation 6 | and threadsafe execution of machine methods. Additionally, combinations of all those features are possible 7 | and made easier to access with a convenience factory. 8 | """ 9 | 10 | from .diagrams import GraphMachine 11 | from .nesting import HierarchicalMachine 12 | from .locking import LockedMachine 13 | 14 | from .factory import MachineFactory, HierarchicalGraphMachine, LockedHierarchicalGraphMachine 15 | from .factory import LockedHierarchicalMachine, LockedGraphMachine 16 | -------------------------------------------------------------------------------- /allzpark/resources/expand.svg: -------------------------------------------------------------------------------- 1 | expand 2 | -------------------------------------------------------------------------------- /allzpark/resources/gear-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /allzpark/resources/profile_current_star.svg: -------------------------------------------------------------------------------- 1 | profile_green_star -------------------------------------------------------------------------------- /allzpark/resources/profile_normal_star.svg: -------------------------------------------------------------------------------- 1 | profile_normal_star -------------------------------------------------------------------------------- /allzpark/resources/version_on.svg: -------------------------------------------------------------------------------- 1 | version_on 2 | -------------------------------------------------------------------------------- /allzpark/resources/version_off.svg: -------------------------------------------------------------------------------- 1 | version_off 2 | -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2018 Benjamin Peterson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /allzpark/vendor/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Marcus Ottosson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /allzpark/resources/refresh.svg: -------------------------------------------------------------------------------- 1 | refresh 2 | -------------------------------------------------------------------------------- /allzpark/version.py: -------------------------------------------------------------------------------- 1 | """Version includes the Git revision number 2 | 3 | This module separates between deployed and development versions of allzpark. 4 | A development version draws its minor version directly from Git, the total 5 | number of commits on the current branch equals the revision number. Once 6 | deployed, this number is embedded into the Python package. 7 | 8 | """ 9 | 10 | version = "1.3" 11 | 12 | try: 13 | # Look for serialised version 14 | from .__version__ import version 15 | 16 | except ImportError: 17 | # Else, we're likely running out of a Git repository 18 | import os as _os 19 | import subprocess as _subprocess 20 | 21 | try: 22 | # If used as a git repository 23 | _cwd = _os.path.dirname(__file__) 24 | _patch = int(_subprocess.check_output( 25 | ["git", "rev-list", "HEAD", "--count"], 26 | 27 | cwd=_cwd, 28 | 29 | # Ensure strings are returned from both Python 2 and 3 30 | universal_newlines=True, 31 | 32 | ).rstrip()) 33 | 34 | # Builds since previous minor version 35 | _patch -= 323 36 | 37 | except Exception: 38 | # Otherwise, no big deal 39 | pass 40 | 41 | else: 42 | version += ".%s" % _patch 43 | -------------------------------------------------------------------------------- /docs/theme/rain.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | height: 100%; 7 | margin: 0; 8 | overflow: hidden; 9 | background: transparent; 10 | } 11 | 12 | .rain { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | height: 100%; 17 | z-index: 2; 18 | } 19 | 20 | .rain.back-row { 21 | display: none; 22 | z-index: 1; 23 | bottom: 60px; 24 | opacity: 0.5; 25 | } 26 | 27 | body.back-row-toggle .rain.back-row { 28 | display: block; 29 | } 30 | 31 | .drop { 32 | position: absolute; 33 | bottom: 0%; 34 | width: 15px; 35 | height: 120px; 36 | pointer-events: none; 37 | animation: drop 10.5s ease-out infinite; 38 | } 39 | 40 | @keyframes drop { 41 | 0% { 42 | transform: translateY(90vh); 43 | } 44 | 75% { 45 | transform: translateY(0vh); 46 | } 47 | 100% { 48 | transform: translateY(0vh); 49 | } 50 | } 51 | 52 | .stem { 53 | width: 3px; 54 | height: 60%; 55 | margin-left: 7px; 56 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25)); 57 | animation: stem 12.5s linear infinite; 58 | } 59 | 60 | @keyframes stem { 61 | 0% { 62 | opacity: 1; 63 | } 64 | 65% { 65 | opacity: 1; 66 | } 67 | 75% { 68 | opacity: 0; 69 | } 70 | 100% { 71 | opacity: 0; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /allzpark/resources/filter_off.svg: -------------------------------------------------------------------------------- 1 | filter_off 2 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions-0.6.9.dist-info/LICENSE: -------------------------------------------------------------------------------- 1 | ******* 2 | License 3 | ******* 4 | 5 | The transitions package, including all examples, code snippets and attached 6 | documentation is covered by the MIT license. 7 | 8 | :: 9 | 10 | The MIT License 11 | 12 | Copyright (c) 2014 Tal Yarkoni 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. -------------------------------------------------------------------------------- /allzpark/vendor/transitions-0.6.9.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | transitions-0.6.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | transitions-0.6.9.dist-info/LICENSE,sha256=ciohQovOOBN70sFNEIF4RpOedzz6FpFlvbvPlIrWF4U,1257 3 | transitions-0.6.9.dist-info/METADATA,sha256=x5TPVRyu0kPJsmILP8OKGndNzzO-Zz3tcWV7pS9Ayq0,54361 4 | transitions-0.6.9.dist-info/RECORD,, 5 | transitions-0.6.9.dist-info/WHEEL,sha256=CihQvCnsGZQBGAHLEUMf0IdA4fRduS_NBUTMgCTtvPM,110 6 | transitions-0.6.9.dist-info/top_level.txt,sha256=NPNpGAXjBpdh1r4K9qdE3wR5wqYX0n7tqMQijL9ZpDM,12 7 | transitions/__init__.py,sha256=jUd6DXaM1UCprwD6m21IhWGQOasZTu5sQxCufGveEMI,494 8 | transitions/__init__.pyc,, 9 | transitions/core.py,sha256=PEsuYYI2XKXN-Ah5gNZ3yyJech3bfroWvGBfnMzfsW4,51680 10 | transitions/core.pyc,, 11 | transitions/extensions/__init__.py,sha256=dEvmUDSxun4ocSAGP_3Hi_MX-T8VB4o6ZZ9T5ljW6co,594 12 | transitions/extensions/__init__.pyc,, 13 | transitions/extensions/diagrams.py,sha256=E50gE8JQhUkdXC0iStdUTgSm1z_Fpq71bdcFBg__nxQ,21650 14 | transitions/extensions/diagrams.pyc,, 15 | transitions/extensions/factory.py,sha256=7Cn0bWIz9UYQ37YHqmLrrP6Ai-s3F9v84cPPvZs14Eo,2900 16 | transitions/extensions/factory.pyc,, 17 | transitions/extensions/locking.py,sha256=Sg6ceynG1Uzzx_H07fYnkR4wyJMhaFUqiGgFFWRmQ50,6224 18 | transitions/extensions/locking.pyc,, 19 | transitions/extensions/nesting.py,sha256=_rJRXtHrtycx-pWQgIbh7QPpleJf2t9C7gGKgta5sgQ,24222 20 | transitions/extensions/nesting.pyc,, 21 | transitions/extensions/states.py,sha256=uO4hNqy8K02O4M7mZflx1MeR3-sxs_XTTxFn0c17wR4,7069 22 | transitions/extensions/states.pyc,, 23 | transitions/version.py,sha256=nJ2iAUOqCzcfWyDxjxdWONi-nGsrwBXs1AIaECw9DN8,176 24 | transitions/version.pyc,, 25 | -------------------------------------------------------------------------------- /tests/test_launch.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | from tests import util 4 | 5 | 6 | class TestLaunch(util.TestBase): 7 | 8 | def test_launch_subprocess(self): 9 | """Test launching subprocess command""" 10 | util.memory_repository({ 11 | "foo": { 12 | "1": { 13 | "name": "foo", 14 | "version": "1", 15 | "requires": ["~app"], 16 | } 17 | }, 18 | "app": { 19 | "1": { 20 | "name": "app", 21 | "version": "1", 22 | } 23 | }, 24 | }) 25 | self.ctrl_reset(["foo"]) 26 | 27 | with self.wait_signal(self.ctrl.state_changed, "ready"): 28 | self.ctrl.select_profile("foo") 29 | 30 | self.ctrl.select_application("app==1") 31 | self.assertEqual("app==1", self.ctrl.state["appRequest"]) 32 | 33 | commands = self.ctrl.state["commands"] 34 | self.assertEqual(len(commands), 0) 35 | 36 | stdout = list() 37 | stderr = list() 38 | command = ( 39 | '%s -c "' 40 | 'import sys;' 41 | 'sys.stdout.write(\'meow\')"' 42 | ) % sys.executable 43 | 44 | with self.wait_signal(self.ctrl.state_changed, "launching"): 45 | self.ctrl.launch(command=command, 46 | stdout=lambda m: stdout.append(m), 47 | stderr=lambda m: stderr.append(m)) 48 | 49 | self.assertEqual(len(commands), 1) 50 | 51 | with self.wait_signal(commands[0].killed): 52 | pass 53 | 54 | self.assertIn("meow", "\n".join(stdout)) 55 | self.assertEqual("", "\n".join(stderr)) 56 | -------------------------------------------------------------------------------- /allzpark/delegates.py: -------------------------------------------------------------------------------- 1 | from .vendor.Qt import QtWidgets, QtCore 2 | 3 | 4 | class Package(QtWidgets.QStyledItemDelegate): 5 | 6 | editor_created = QtCore.Signal() 7 | editor_closed = QtCore.Signal(bool) 8 | 9 | def __init__(self, ctrl, parent=None): 10 | super(Package, self).__init__(parent) 11 | 12 | def on_close_editor(*args): 13 | self.editor_closed.emit(self._changed) 14 | self.closeEditor.connect(on_close_editor) 15 | 16 | self._changed = None 17 | self._default = None 18 | self._ctrl = ctrl 19 | 20 | def createEditor(self, parent, option, index): 21 | model = index.model() 22 | if index.column() != 1 or not model.data(index, "_hasVersions"): 23 | return 24 | 25 | editor = QtWidgets.QComboBox(parent) 26 | 27 | def on_text_activated(text): 28 | self._changed = text != self._default 29 | editor.textActivated.connect(on_text_activated) 30 | 31 | return editor 32 | 33 | def setEditorData(self, editor, index): 34 | model = index.model() 35 | options = model.data(index, "versions") 36 | default = index.data(QtCore.Qt.DisplayRole) 37 | 38 | self._changed = False 39 | self._default = default 40 | 41 | editor.addItems(options) 42 | editor.setCurrentIndex(options.index(default)) 43 | 44 | self.editor_created.emit() 45 | 46 | def setModelData(self, editor, model, index): 47 | model = index.model() 48 | package = model.data(index, "family") 49 | options = model.data(index, "versions") 50 | default = model.data(index, "default") 51 | version = options[editor.currentIndex()] 52 | 53 | if not version or version == default: 54 | return 55 | 56 | self._ctrl.patch("%s==%s" % (package, version)) 57 | -------------------------------------------------------------------------------- /docs/theme/landing.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | {% extends "base.html" %} 10 | 11 | {% block site_meta %} 12 | {{ super() }} 13 | 14 | {% endblock %} 15 | 16 | {% block site_nav %} 17 | {% if nav %} 18 |
19 |
20 |
21 | {% include "partials/nav.html" %} 22 |
23 |
24 |
25 | {% endif %} 26 | {% if page.toc %} 27 |
28 |
29 |
30 | {% include "partials/toc.html" %} 31 |
32 |
33 |
34 | {% endif %} 35 | {% endblock %} 36 | 37 | {% block htmltitle %} 38 | {{ config.site_name }} 39 | {% endblock %} 40 | 41 | 56 | 57 | {% block styles %} 58 | {{ super() }} 59 | 65 | 66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | site_name: Allzpark 3 | site_description: The package-based application launcher 4 | site_author: mottosso 5 | site_url: https://allzpark.com 6 | 7 | # Repository 8 | repo_name: mottosso/allzpark 9 | repo_url: https://github.com/mottosso/allzpark 10 | 11 | copyright: "Copyright © 2019 mottosso" 12 | 13 | # Page tree 14 | nav: 15 | - Home: index.md 16 | - Quickstart: quickstart.md 17 | - Getting Started: getting-started.md 18 | - Getting Advanced: getting-advanced.md 19 | - Getting Rez'd: rez.md 20 | - User Interface: gui.md 21 | - Workflow: workflow.md 22 | - Reference: reference.md 23 | - Shells: shells.md 24 | - Windows: windows.md 25 | - Contributing: contributing.md 26 | - About: about.md 27 | 28 | docs_dir: pages 29 | edit_uri: edit/master/docs/pages/ 30 | 31 | theme: 32 | name: material 33 | logo: logo.png 34 | custom_dir: theme 35 | palette: 36 | primary: "white" 37 | accent: "white" 38 | 39 | extra_css: 40 | - extra.css 41 | # Landing page includes additional CSS from landing.html 42 | 43 | # Options 44 | extra: 45 | feature: 46 | tabs: true 47 | 48 | extra_javascript: 49 | - "extra.js" 50 | 51 | markdown_extensions: 52 | - codehilite: 53 | guess_lang: false 54 | use_pygments: true 55 | noclasses: true 56 | # linenums: true 57 | pygments_style: monokai 58 | - markdown.extensions.extra 59 | - markdown.extensions.admonition 60 | - markdown.extensions.def_list 61 | - markdown.extensions.footnotes 62 | - markdown.extensions.meta 63 | - markdown.extensions.toc 64 | - pymdownx.arithmatex 65 | - pymdownx.betterem 66 | - pymdownx.caret 67 | - pymdownx.critic 68 | - pymdownx.details 69 | - pymdownx.emoji: 70 | emoji_generator: !!python/name:pymdownx.emoji.to_svg 71 | - pymdownx.inlinehilite 72 | - pymdownx.magiclink 73 | - pymdownx.mark 74 | - pymdownx.smartsymbols 75 | - pymdownx.superfences 76 | - pymdownx.tasklist 77 | - pymdownx.tilde 78 | 79 | plugins: 80 | - search 81 | - git-revision-date 82 | -------------------------------------------------------------------------------- /docs/pages/workflow.md: -------------------------------------------------------------------------------- 1 | This page is dedicated to best practices for working with Rez and Allzpark. 2 | 3 |
4 | 5 | ### Development Workflow 6 | 7 | Whether you are writing a C++ or Python library, the general workflow goes something like this. 8 | 9 | 1. Edit source 10 | 2. Call `rez build --install` 11 | 3. Reload library (e.g. Maya module or plug-in) 12 | 4. Test the library 13 | 5. Goto (1) until satisfied 14 | 15 | Where `rez build --install` operates in two steps. 16 | 17 | 1. Create an internal `build\` directory with everything going into the installed package 18 | 2. Copy the `build\` directory into your `REZ_LOCAL_PACKAGES_PATH` as the specified version 19 | 20 | This operation is destructive, in that it will overwrite what was already there. This is what allows you to keep rebuilding and testing your package, without having to increment the version for each line of source code changed. 21 | 22 |
23 | 24 | ### Release Workflow 25 | 26 | One of the strenghts of Rez is that, whilst Git enables you to version control changes to code, Rez enables version control released software onto the floor. Once you're happy with an update or newly created package, it's time to release it. 27 | 28 | ```bash 29 | cd my_package 30 | rez build --install --release 31 | ``` 32 | 33 | This command will perform an identical operation to `rez build --install`, except packages written to `REZ_RELEASE_PACKAGES_PATH`. 34 | 35 | Like `--install`, this operation is destructive, and most of the time you wouldn't want released packages to change. There are at least two methods for preventing that, both of which involve the use of **Git**. 36 | 37 | 1. Limit use of `--release` to a server-side application, like GitLab. For example, once your changes have been pushed and tagged, a CI pipeline could trigger a call to `--release` whereby the GitLab runner is the only one with write-access to the `REZ_RELEASE_PACKAGES_PATH`. 38 | 2. Use `rez release` which does something similar, except locally. It creates a tag identical to the proposed package version, pushed it to the currently set up Git remote. -------------------------------------------------------------------------------- /docs/theme/extra.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | 3 | var OSName = "powershell"; 4 | if (window.navigator.userAgent.indexOf("Windows") != -1) OSName = "powershell"; 5 | if (window.navigator.userAgent.indexOf("Mac") != -1) OSName = "bash"; 6 | if (window.navigator.userAgent.indexOf("X11") != -1) OSName = "bash"; 7 | if (window.navigator.userAgent.indexOf("Linux") != -1) OSName = "bash"; 8 | 9 | // Per default, display tab most relevant to the current OS 10 | var defaultTab = document.getElementsByClassName("tab " + OSName); 11 | for (i = 0; i < defaultTab.length; i++){ 12 | defaultTab[i].click(); 13 | break; 14 | } 15 | }, false ); 16 | 17 | /* Utility function */ 18 | function hasClass(element, cls) { 19 | return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; 20 | } 21 | 22 | /* Called when the user clicks any tabbed element */ 23 | function setTab(event, tabName) { 24 | var i, tabcontent, tabs; 25 | 26 | // Get all elements with class="tabcontent" and hide them 27 | tabcontent = document.getElementsByClassName("tab-content"); 28 | for (i = 0; i < tabcontent.length; i++) { 29 | tabcontent[i].style.display = hasClass(tabcontent[i], tabName) ? "block" : "none"; 30 | } 31 | 32 | // Get all elements with class="tabs" and remove the class "active" 33 | tabs = document.getElementsByClassName("tab"); 34 | for (i = 0; i < tabs.length; i++) { 35 | tabs[i].className = tabs[i].className.replace(" active", ""); 36 | tabs[i].className += hasClass(tabs[i], tabName) ? " active" : "" 37 | } 38 | } 39 | 40 | /* Called when the user clicks a spoiler button */ 41 | function reveal(event, id) { 42 | var button = event.currentTarget; 43 | 44 | if (hasClass(button, "revealed")) { 45 | button.className = button.className.replace(" revealed", ""); 46 | } else { 47 | button.className += " revealed"; 48 | } 49 | 50 | var content = document.getElementById(id); 51 | 52 | if (hasClass(content, "hidden")) { 53 | content.className = content.className.replace(" hidden", ""); 54 | } else { 55 | content.className += " hidden"; 56 | } 57 | } -------------------------------------------------------------------------------- /allzpark/vendor/six-1.12.0.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: six 3 | Version: 1.12.0 4 | Summary: Python 2 and 3 compatibility utilities 5 | Home-page: https://github.com/benjaminp/six 6 | Author: Benjamin Peterson 7 | Author-email: benjamin@python.org 8 | License: MIT 9 | Platform: UNKNOWN 10 | Classifier: Development Status :: 5 - Production/Stable 11 | Classifier: Programming Language :: Python :: 2 12 | Classifier: Programming Language :: Python :: 3 13 | Classifier: Intended Audience :: Developers 14 | Classifier: License :: OSI Approved :: MIT License 15 | Classifier: Topic :: Software Development :: Libraries 16 | Classifier: Topic :: Utilities 17 | Requires-Python: >=2.6, !=3.0.*, !=3.1.* 18 | 19 | .. image:: https://img.shields.io/pypi/v/six.svg 20 | :target: https://pypi.org/project/six/ 21 | :alt: six on PyPI 22 | 23 | .. image:: https://travis-ci.org/benjaminp/six.svg?branch=master 24 | :target: https://travis-ci.org/benjaminp/six 25 | :alt: six on TravisCI 26 | 27 | .. image:: https://readthedocs.org/projects/six/badge/?version=latest 28 | :target: https://six.readthedocs.io/ 29 | :alt: six's documentation on Read the Docs 30 | 31 | .. image:: https://img.shields.io/badge/license-MIT-green.svg 32 | :target: https://github.com/benjaminp/six/blob/master/LICENSE 33 | :alt: MIT License badge 34 | 35 | Six is a Python 2 and 3 compatibility library. It provides utility functions 36 | for smoothing over the differences between the Python versions with the goal of 37 | writing Python code that is compatible on both Python versions. See the 38 | documentation for more information on what is provided. 39 | 40 | Six supports every Python version since 2.6. It is contained in only one Python 41 | file, so it can be easily copied into your project. (The copyright and license 42 | notice must be retained.) 43 | 44 | Online documentation is at https://six.readthedocs.io/. 45 | 46 | Bugs can be reported to https://github.com/benjaminp/six. The code can also 47 | be found there. 48 | 49 | For questions about six or porting in general, email the python-porting mailing 50 | list: https://mail.python.org/mailman/listinfo/python-porting 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/theme/rain.js: -------------------------------------------------------------------------------- 1 | var makeItRain = function() { 2 | //clear out everything 3 | $('.rain').empty(); 4 | 5 | var increment = 0; 6 | var drops = ""; 7 | var backDrops = ""; 8 | 9 | while (increment < 100) { 10 | //couple random numbers to use for various randomizations 11 | //random number between 98 and 1 12 | var randoHundo = (Math.floor(Math.random() * (98 - 1 + 1) + 1)); 13 | //random number between 5 and 2 14 | var randoFiver = (Math.floor(Math.random() * (5 - 2 + 1) + 2)); 15 | //increment 16 | increment += randoFiver; 17 | //add in a new raindrop with various randomizations to certain CSS properties 18 | drops += '
'; 19 | backDrops += '
'; 20 | } 21 | 22 | $('.rain.front-row').append(drops); 23 | $('.rain.back-row').append(backDrops); 24 | } 25 | 26 | $('.splat-toggle.toggle').on('click', function() { 27 | $('body').toggleClass('splat-toggle'); 28 | $('.splat-toggle.toggle').toggleClass('active'); 29 | makeItRain(); 30 | }); 31 | 32 | $('.back-row-toggle.toggle').on('click', function() { 33 | $('body').toggleClass('back-row-toggle'); 34 | $('.back-row-toggle.toggle').toggleClass('active'); 35 | makeItRain(); 36 | }); 37 | 38 | $('.single-toggle.toggle').on('click', function() { 39 | $('body').toggleClass('single-toggle'); 40 | $('.single-toggle.toggle').toggleClass('active'); 41 | makeItRain(); 42 | }); 43 | 44 | makeItRain(); -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A cross-platform launcher for film and games projects, built on Rez""" 2 | 3 | import os 4 | from setuptools import setup, find_packages 5 | from allzpark.version import version 6 | 7 | # Git is required for deployment 8 | assert len(version.split(".")) == 3, ( 9 | "Could not compute patch version, make sure `git` is\n" 10 | "available and see version.py for details") 11 | 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 15 | "Intended Audience :: Developers", 16 | "Operating System :: OS Independent", 17 | "Programming Language :: Python", 18 | "Programming Language :: Python :: 2", 19 | "Programming Language :: Python :: 2.7", 20 | "Programming Language :: Python :: 3", 21 | "Programming Language :: Python :: 3.6", 22 | "Programming Language :: Python :: 3.7", 23 | "Topic :: Utilities", 24 | "Topic :: Software Development", 25 | "Topic :: Software Development :: Libraries :: Python Modules", 26 | ] 27 | 28 | # Store version alongside package 29 | dirname = os.path.dirname(__file__) 30 | fname = os.path.join(dirname, "allzpark", "__version__.py") 31 | with open(fname, "w") as f: 32 | f.write("version = \"%s\"\n" % version) 33 | 34 | setup( 35 | name="allzpark", 36 | version=version, 37 | description=__doc__, 38 | keywords="launcher package resolve version software management", 39 | long_description=__doc__, 40 | url="https://github.com/mottosso/allzpark", 41 | author="Marcus Ottosson", 42 | author_email="konstruktion@gmail.com", 43 | license="LGPL", 44 | zip_safe=False, 45 | packages=find_packages(), 46 | package_data={ 47 | "allzpark": [ 48 | "resources/*.png", 49 | "resources/*.css", 50 | "resources/*.svg", 51 | "resources/fonts/*/*.ttf", 52 | ] 53 | }, 54 | entry_points={ 55 | "console_scripts": [ 56 | "allzpark = allzpark.cli:main", 57 | 58 | # Alias 59 | "azp = allzpark.cli:main", 60 | ] 61 | }, 62 | classifiers=classifiers, 63 | install_requires=[ 64 | "bleeding-rez>=2.38.2", 65 | "allzparkdemo>=1", 66 | 67 | # Specifically for Python 2.. 68 | "PySide; python_version<'3'", 69 | 70 | # ..and likewise for Python 3 71 | "PySide2; python_version>'3'", 72 | ], 73 | python_requires=">2.7, <4", 74 | ) 75 | -------------------------------------------------------------------------------- /docs/pages/quickstart.md: -------------------------------------------------------------------------------- 1 | This page will get you up and running with Allzpark in less than 2 minutes. 2 | 3 |
4 | 5 | ### Quickstart 6 | 7 | The below commands will install Allzpark and its dependencies, including Rez. 8 | 9 | ```bash 10 | python -m pip install allzpark --upgrade 11 | rez bind --quickstart 12 | allzpark --demo --clean 13 | ``` 14 | 15 | > Skip the `--clean` flag to preserve preferences, such as window layout, between runs. 16 | 17 |
18 | 19 | #### Troubleshooting 20 | 21 | Everything ok? 22 | 23 | ??? quote "No module named pip" 24 | For this to work, we'll need pip. 25 | 26 | - [Reference](https://pip.pypa.io/en/stable/installing/) 27 | 28 | ```bash 29 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 30 | python get-pip.py 31 | ``` 32 | 33 | If that didn't work, have a look at to install pip for your platform. 34 | 35 | **Examples** 36 | 37 | - CentOS 7 - `yum install python-pip` 38 | - Ubuntu 18 - `apt install python3-pip` 39 | 40 | ??? quote "Permission denied" 41 | The above command assumes admin/sudo access to your machine which isn't always the case. If so, you can install Allzpark into a virtual environment. 42 | 43 | **Python 3** 44 | 45 | ```bash 46 | $ python -m venv allzpark-venv 47 | $ allzpark-venv\Scripts\activate 48 | (allzpark-venv) $ pip install allzpark 49 | ``` 50 | 51 | **Python 2** 52 | 53 | ```bash 54 | $ python -m pip install virtualenv 55 | $ python -m virtualenv allzpark-venv 56 | $ allzpark-venv\Scripts\activate 57 | (allzpark-venv) $ pip install allzpark 58 | ``` 59 | 60 | ??? quote "rez not found" 61 | If installation went successfully, but you aren't able to call `rez` then odds are the Python executable path isn't on your `PATH`. On Windows, this directory is typically at `c:\python37\scripts` but may vary depending on how Python was installed, and varies across platforms. 62 | 63 | Following the installation of `rez`, you should have gotten a message about which path was missing from your `PATH`, you can either add this yourself, or use the `virtualenv` method from the above `Permission denied` box. 64 | 65 | **Example message** 66 | 67 | ```powershell 68 | The script allzpark.exe and azp.exe are installed in 'C:\Python37\Scripts' which is not on PATH 69 | Consider adding this directory to PATH 70 | ``` 71 | 72 | ??? quote "Something else happened" 73 | Oh no! I'd like to know about what happened, please let me know [here](https://github.com/mottosso/allzpark/issues). 74 | 75 |
76 | 77 | #### Result 78 | 79 | If everything went well, you should now be presented with this! 80 | 81 | ![image](https://user-images.githubusercontent.com/2152766/61855839-c68f3400-aeb8-11e9-9df5-d31a39b6e028.png) 82 | 83 |
84 | 85 | ### Next Steps 86 | 87 | From here, try launching your favourite application, navigate the interface and make yourself at home. Then have a look at these to learn more. 88 | 89 | > Note that the applications provided are examples and may not work as-is on your system. 90 | 91 | - [Create a new profile](/getting-started) 92 | - [Create a new application](/getting-started/#your-first-application) 93 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions/extensions/factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | transitions.extensions.factory 3 | ------------------------------ 4 | 5 | This module contains the definitions of classes which combine the functionality of transitions' 6 | extension modules. These classes can be accessed by names as well as through a static convenience 7 | factory object. 8 | """ 9 | 10 | from ..core import Machine 11 | 12 | from .nesting import HierarchicalMachine, NestedTransition, NestedEvent 13 | from .locking import LockedMachine, LockedEvent 14 | from .diagrams import GraphMachine, TransitionGraphSupport, NestedGraph 15 | 16 | 17 | class MachineFactory(object): 18 | """ 19 | Convenience factory for machine class retrieval. 20 | """ 21 | 22 | # get one of the predefined classes which fulfill the criteria 23 | @staticmethod 24 | def get_predefined(graph=False, nested=False, locked=False): 25 | """ A function to retrieve machine classes by required functionality. 26 | Args: 27 | graph (bool): Whether the returned class should contain graph support. 28 | nested: Whether the returned machine class should support nested states. 29 | locked: Whether the returned class should facilitate locks for threadsafety. 30 | 31 | Returns (class): A machine class with the specified features. 32 | """ 33 | return _CLASS_MAP[(graph, nested, locked)] 34 | 35 | 36 | class NestedGraphTransition(TransitionGraphSupport, NestedTransition): 37 | """ 38 | A transition type to be used with (subclasses of) `HierarchicalGraphMachine` and 39 | `LockedHierarchicalGraphMachine`. 40 | """ 41 | pass 42 | 43 | 44 | class LockedNestedEvent(LockedEvent, NestedEvent): 45 | """ 46 | An event type to be used with (subclasses of) `LockedHierarchicalMachine` 47 | and `LockedHierarchicalGraphMachine`. 48 | """ 49 | pass 50 | 51 | 52 | class HierarchicalGraphMachine(GraphMachine, HierarchicalMachine): 53 | """ 54 | A hierarchical state machine with graph support. 55 | """ 56 | 57 | transition_cls = NestedGraphTransition 58 | graph_cls = NestedGraph 59 | 60 | 61 | class LockedHierarchicalMachine(LockedMachine, HierarchicalMachine): 62 | """ 63 | A threadsafe hierarchical machine. 64 | """ 65 | 66 | event_cls = LockedNestedEvent 67 | 68 | 69 | class LockedGraphMachine(GraphMachine, LockedMachine): 70 | """ 71 | A threadsafe machine with graph support. 72 | """ 73 | pass 74 | 75 | 76 | class LockedHierarchicalGraphMachine(GraphMachine, LockedMachine, HierarchicalMachine): 77 | """ 78 | A threadsafe hiearchical machine with graph support. 79 | """ 80 | 81 | transition_cls = NestedGraphTransition 82 | event_cls = LockedNestedEvent 83 | graph_cls = NestedGraph 84 | 85 | 86 | # 3d tuple (graph, nested, locked) 87 | _CLASS_MAP = { 88 | (False, False, False): Machine, 89 | (False, False, True): LockedMachine, 90 | (False, True, False): HierarchicalMachine, 91 | (False, True, True): LockedHierarchicalMachine, 92 | (True, False, False): GraphMachine, 93 | (True, False, True): LockedGraphMachine, 94 | (True, True, False): HierarchicalGraphMachine, 95 | (True, True, True): LockedHierarchicalGraphMachine 96 | } 97 | -------------------------------------------------------------------------------- /docs/pages/gui.md: -------------------------------------------------------------------------------- 1 | This page is specifically about the Allzpark graphical user interface. 2 | 3 |
4 | 5 | ### Advanced Controls 6 | 7 | In the [Preferences](#preferences) you'll find an option to enable "Advanced Controls". These are designed to separate what is useful to an artist versus a developer. 8 | 9 | ![image](https://user-images.githubusercontent.com/2152766/61855966-0bb36600-aeb9-11e9-8202-0bd9e6c98bed.png) 10 | 11 |
12 | 13 | ### Multiple Application Versions 14 | 15 | Sometimes you need multiple versions of a single application accessible via the same profile. Per default, Allzpark only displays the latest version of the versions available. 16 | 17 | **allzparkconfig.py** 18 | 19 | Here's the default. 20 | 21 | ```py 22 | def applications_from_package(variant): 23 | requirements = variant.requires or [] 24 | 25 | apps = list( 26 | str(req) 27 | for req in requirements 28 | if req.weak 29 | ) 30 | 31 | return apps 32 | ``` 33 | 34 | **allzparkconfig.py** 35 | 36 | As you can see, the default only returns the one request for an application. But Allzpark will display every version you return, here's an example of that. 37 | 38 | ```py 39 | def applications_from_package(variant): 40 | from allzpark import _rezapi as rez 41 | 42 | # May not be defined 43 | requirements = variant.requires or [] 44 | 45 | apps = list( 46 | str(req) 47 | for req in requirements 48 | if req.weak 49 | ) 50 | 51 | # Strip the "weak" property of the request, else iter_packages 52 | # isn't able to find the requested versions. 53 | apps = [rez.PackageRequest(req.strip("~")) for req in apps] 54 | 55 | # Expand versions into their full range 56 | # E.g. maya-2018|2019 == ["maya-2018", "maya-2019"] 57 | flattened = list() 58 | for request in apps: 59 | flattened += rez.find( 60 | request.name, 61 | range_=request.range, 62 | ) 63 | 64 | # Return strings 65 | apps = list( 66 | "%s==%s" % (package.name, package.version) 67 | for package in flattened 68 | ) 69 | 70 | return apps 71 | ``` 72 | 73 |
74 | 75 | ### Applications from Data 76 | 77 | In addition to the above, you could also specify applications explicitly in your profile data. 78 | 79 | **Alita/package.py** 80 | 81 | ```py 82 | name = "alita" 83 | version = "1.0" 84 | _data = { 85 | "apps": ["maya-2018", "vs-2019", "zbrush", "mudbox"] 86 | } 87 | ``` 88 | 89 | **allzparkconfig.py** 90 | 91 | ```py 92 | def applications_from_package(package): 93 | try: 94 | return package._data["apps"] 95 | 96 | except (AttributeError, KeyError): 97 | # If there isn't any data, just do what you normally do 98 | from allzpark.allzparkconfig import _applications_from_package 99 | 100 | # Every variable and function from allzparkconfig has this hidden 101 | # alternative reference, with a "_" prefix. 102 | return _applications_from_package(package) 103 | ``` 104 | 105 | And as a side-note, here you can also return multiple versions of a given application. 106 | 107 | ```py 108 | name = "alita" 109 | version = "1.0" 110 | _data = { 111 | "apps": [ 112 | "maya-2018", 113 | "maya-2019", 114 | "maya-2020", 115 | ] 116 | } 117 | ``` 118 | -------------------------------------------------------------------------------- /docs/pages/getting-advanced.md: -------------------------------------------------------------------------------- 1 | This page carries on from a successful 👈 [Getting Started](/getting-started) into making it relevant for use in VFX and games production. 2 | 3 |
4 | 5 | ### Getting Advanced 6 | 7 | So you've taken Allzpark for a spin and found that it is wonderful, good for you! Here's how you can take it to the next level, by learning about how Allzpark and Rez works in a shared environment. 8 | 9 |
10 | 11 | ### Performance pt. 1 12 | 13 | - [ ] Memcached and why you need it 14 | - [ ] Memcached configuration recommendations 15 | 16 | !!! note "Work in progress" 17 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez and `memcached`, help out by [contributing to the project](/contributing). 18 | 19 |
20 | 21 | ### Performance pt. 2 22 | 23 | - [ ] Packages are folders on your filesystem, your filesystem is slow 24 | - [ ] Localisation and remote labour 25 | 26 | !!! note "Work in progress" 27 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section. 28 | 29 |
30 | 31 | ### Releasing Packages 32 | 33 | - [ ] Shared package repository 34 | - [ ] Read/write permissions 35 | - [ ] Automatic relase via GitLab CI 36 | 37 | !!! note "Work in progress" 38 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez and `rez release` or `rez build --prefix`, help out by [contributing to the project](/contributing). 39 | 40 |
41 | 42 | ### Rezifying Allzpark 43 | 44 | Learn how to close the loop and make Allzpark into just another Rez package. 45 | 46 | !!! note "Work in progress" 47 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez, help out by [contributing to the project](/contributing). 48 | 49 |
50 | 51 | ### Rezifying Rez 52 | 53 | Turn the loop into a spiral by making Rez another shared package. Key point here being, how can you keep everyone on the same version of Rez, if Rez is the thing keeping everyone at the same version of software? 54 | 55 | - [ ] Rez is just another Python package, can reside anywhere 56 | - [ ] So can Python 57 | - [ ] Only thing to keep in mind is performance; on Windows more of an issue, on Linux not so much 58 | 59 | !!! note "Work in progress" 60 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez, help out by [contributing to the project](/contributing). 61 | 62 |
63 | 64 | ### Beta Package 65 | 66 | Learn about how to release packages to a smaller test audience without disrupting normal operation, with the `.beta` suffix. 67 | 68 | !!! note "Work in progress" 69 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez, help out by [contributing to the project](/contributing). 70 | 71 |
72 | 73 | ### Package Encapsulation 74 | 75 | Learn about the importance and utility of keeping packages self-contained and not reference anything outside of its own root directory. 76 | 77 | !!! note "Work in progress" 78 | [Let me know](https://github.com/mottosso/allzpark/issues) if you would like me to flesh out this section, or if you are already familiar with Rez, help out by [contributing to the project](/contributing). 79 | 80 |
-------------------------------------------------------------------------------- /allzpark/_rezapi.py: -------------------------------------------------------------------------------- 1 | # API wrapper for Rez 2 | 3 | from rez.resolved_context import ResolvedContext as env 4 | from rez.packages_ import iter_packages as find 5 | from rez.package_copy import copy_package 6 | from rez.package_filter import Rule, PackageFilterList 7 | from rez.package_repository import package_repository_manager 8 | from rez.packages_ import Package 9 | from rez.utils.formatting import PackageRequest 10 | from rez.system import system 11 | from rez.config import config 12 | from rez.util import which 13 | from rez import __version__ as version 14 | from rez.exceptions import ( 15 | PackageFamilyNotFoundError, 16 | RexUndefinedVariableError, 17 | ResolvedContextError, 18 | RexError, 19 | PackageCommandError, 20 | PackageRequestError, 21 | PackageNotFoundError, 22 | RezError, 23 | ) 24 | from rez.utils.graph_utils import save_graph 25 | 26 | 27 | def clear_caches(): 28 | for path in config.packages_path: 29 | repo = package_repository_manager.get_repository(path) 30 | repo.clear_caches() 31 | 32 | 33 | def find_one(name, range_=None, paths=None, package_filter=None): 34 | """ 35 | Find next package version 36 | 37 | Args: 38 | name (str): Name of the rez package 39 | range_ (VersionRange or str, optional): Limits versions to range 40 | paths (list of str, optional): Paths to search for packages 41 | package_filter (PackageFilter, optional): Limits versions to those 42 | that match package filter 43 | 44 | Returns: 45 | rez.packages_.Package 46 | """ 47 | if package_filter: 48 | return next(package_filter.iter_packages(name, range_, paths)) 49 | else: 50 | return next(find(name, range_, paths)) 51 | 52 | 53 | def find_latest(name, range_=None, paths=None, package_filter=None): 54 | """ 55 | Find latest package version 56 | 57 | Args: 58 | name (str): Name of the rez package 59 | range_ (VersionRange or str, optional): Limits versions to range 60 | paths (list of str, optional): Paths to search for packages 61 | package_filter (PackageFilter, optional): Limits versions to those 62 | that match package filter 63 | 64 | Returns: 65 | rez.packages_.Package 66 | """ 67 | if package_filter: 68 | it = package_filter.iter_packages(name, range_, paths) 69 | else: 70 | it = find(name, range_, paths) 71 | 72 | it = sorted(it, key=lambda pkg: pkg.version) 73 | 74 | try: 75 | return list(it)[-1] 76 | except IndexError: 77 | raise PackageNotFoundError( 78 | "package family not found: %s" % name 79 | ) 80 | 81 | 82 | try: 83 | from rez import __project__ as project 84 | except ImportError: 85 | # nerdvegas/rez 86 | project = "rez" 87 | 88 | 89 | __all__ = [ 90 | "env", 91 | "find", 92 | "find_one", 93 | "find_latest", 94 | "config", 95 | "version", 96 | "project", 97 | "copy_package", 98 | "package_repository_manager", 99 | "system", 100 | 101 | # Classes 102 | "Package", 103 | "PackageRequest", 104 | 105 | # Exceptions 106 | "PackageFamilyNotFoundError", 107 | "ResolvedContextError", 108 | "RexUndefinedVariableError", 109 | "RexError", 110 | "PackageCommandError", 111 | "PackageNotFoundError", 112 | "PackageRequestError", 113 | "RezError", 114 | 115 | # Filters 116 | "Rule", 117 | "PackageFilterList", 118 | 119 | # Extras 120 | "which", 121 | "save_graph", 122 | "clear_caches", 123 | ] 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Application launcher and environment management
for 21st century games and digital post-production,
built with bleeding-rez and Qt.py

5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | Windows 13 | Linux 14 | MacOS 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 | #### News 25 | 26 | | Date | Release | Notes 27 | |:-------------|:--------|:---------- 28 | | October 2020 | [1.3](https://github.com/mottosso/allzpark/releases/tag/1.3.0) | Dedicated profiles panel 29 | | August 2019 | 1.2 | First official release 30 | 31 | - See [all releases](https://github.com/mottosso/allzpark/releases) 32 | 33 |
34 | 35 | ### What is it? 36 | 37 | It's an application launcher, for when you need control over what software and which versions of software belong to a given project. It builds on the self-hosted package manager and environment management framework [bleeding-rez](https://github.com/mottosso/bleeding-rez), providing both a visual and textual interface for launching software in a reproducible way. 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 |
47 | 48 | ### Usage 49 | 50 | Allzpark runs on Windows, Linux and MacOS, using Python 2 or 3 and any binding of Qt, and is available via `pip`. 51 | 52 | ```bash 53 | pip install allzpark 54 | ``` 55 | 56 | See [Quickstart](https://allzpark.com/quickstart) for more details and tutorials. 57 | 58 | **Some Table of Contents** 59 | 60 | - [Landing Page](https://allzpark.com) 61 | - [Getting Started](https://allzpark.com/getting-started) 62 | - [Getting Advanced](https://allzpark.com/getting-advanced) 63 | - [Getting Rez'd](https://allzpark.com/rez) 64 | - [Contributing](https://allzpark.com/contributing) 65 | - [...](https://allzpark.com) 66 | 67 |
68 | 69 | ### Updating the Docs 70 | 71 | I'd like for this to happen during CI, but till then there's a `deploy.ps1` in the `docs/` directory. 72 | 73 | ```bash 74 | cd allzpark\docs 75 | . deploy.ps1 76 | ``` 77 | 78 | This will build the docs and deploy it onto the `gh-pages` branch, which is reflected live after about 1 min. 79 | -------------------------------------------------------------------------------- /tests/test_docks.py: -------------------------------------------------------------------------------- 1 | 2 | from tests import util 3 | 4 | 5 | class TestDocks(util.TestBase): 6 | 7 | def test_feature_blocked_on_failed_app(self): 8 | """Test feature blocked if application is broken""" 9 | util.memory_repository({ 10 | "foo": { 11 | "1.0.0": { 12 | "name": "foo", 13 | "version": "1.0.0", 14 | "requires": [ 15 | "~app_A", # missing package (broken app) 16 | "~app_B", 17 | ], 18 | }, 19 | }, 20 | "app_B": {"1": {"name": "app_B", "version": "1"}}, 21 | }) 22 | self.ctrl_reset(["foo"]) 23 | 24 | self.set_preference("showAdvancedControls", True) 25 | 26 | context_a = self.ctrl.state["rezContexts"]["app_A==None"] 27 | context_b = self.ctrl.state["rezContexts"]["app_B==1"] 28 | 29 | self.assertFalse(context_a.success) 30 | self.assertTrue(context_b.success) 31 | 32 | for app, state in {"app_A==None": False, "app_B==1": True}.items(): 33 | self.select_application(app) 34 | 35 | dock = self.show_dock("environment", on_page="diagnose") 36 | self.assertEqual(dock._widgets["compute"].isEnabled(), state) 37 | 38 | dock = self.show_dock("context", on_page="code") 39 | self.assertEqual(dock._widgets["printCode"].isEnabled(), state) 40 | 41 | dock = self.show_dock("app") 42 | self.assertEqual(dock._widgets["launchBtn"].isEnabled(), state) 43 | 44 | def test_version_editable_on_show_all_versions(self): 45 | """Test version is editable when show all version enabled""" 46 | self._test_version_editable(show_all_version=True) 47 | 48 | def test_version_editable_on_not_show_all_versions(self): 49 | """Test version is not editable when show all version disabled""" 50 | self._test_version_editable(show_all_version=False) 51 | 52 | def _test_version_editable(self, show_all_version): 53 | util.memory_repository({ 54 | "foo": { 55 | "1": {"name": "foo", "version": "1", 56 | "requires": ["~app_A", "~app_B"]}, 57 | "2": {"name": "foo", "version": "2", 58 | "requires": ["~app_A", "~app_B"]}, 59 | }, 60 | "app_A": {"1": {"name": "app_A", "version": "1"}}, 61 | "app_B": {"1": {"name": "app_B", "version": "1", 62 | "requires": ["bar"]}}, 63 | "bar": {"1": {"name": "bar", "version": "1"}, 64 | "2": {"name": "bar", "version": "2"}} 65 | }) 66 | self.ctrl_reset(["foo"]) 67 | 68 | self.set_preference("showAdvancedControls", True) 69 | self.set_preference("showAllVersions", show_all_version) 70 | self.wait(200) # wait for reset 71 | 72 | self.select_application("app_B==1") 73 | 74 | dock = self.show_dock("packages") 75 | view = dock._widgets["view"] 76 | proxy = view.model() 77 | model = proxy.sourceModel() 78 | 79 | for pkg, state in {"foo": False, # profile can't change version here 80 | "bar": show_all_version, 81 | "app_B": False}.items(): 82 | index = model.findIndex(pkg) 83 | index = proxy.mapFromSource(index) 84 | 85 | rect = view.visualRect(index) 86 | position = rect.center() 87 | with util.patch_cursor_pos(view.mapToGlobal(position)): 88 | dock.on_right_click(position) 89 | menu = self.get_menu(dock) 90 | edit_action = next((a for a in menu.actions() 91 | if a.text() == "Edit"), None) 92 | if edit_action is None: 93 | self.fail("No version edit action.") 94 | 95 | self.assertEqual( 96 | edit_action.isEnabled(), state, 97 | "Package '%s' version edit state is incorrect." % pkg 98 | ) 99 | 100 | self.wait(200) 101 | menu.close() 102 | -------------------------------------------------------------------------------- /docs/pages/windows.md: -------------------------------------------------------------------------------- 1 | Both Allzpark and Rez are cross-platform, but each platform has a few gotchas to keep in mind. Here's a quick primer on how to make the most out of Allzpark and Rez on the Windows operating system. 2 | 3 |
4 | 5 | ## Long File Paths 6 | 7 | Windows has a max path length of 260 characters, which can become an issue for packages on a long repository path and multiple variants. 8 | 9 |
10 | 11 | ### Problem 12 | 13 | ```bash 14 | # Repository root 15 | \\mylongstudioaddress.local\main\common\utilities\packages\internal 16 | 17 | # Package 18 | \maya_essentials\1.42.5beta\platform-windows\arch-AMD64\os-windows-10.0.1803 19 | 20 | # Payload 21 | \python\maya_essentials\utilities\__init__.py 22 | ``` 23 | 24 | **188 characters** 25 | 26 | That's a relatively common path to a Python package, packaged with Rez, and we're already close to the 260 character limit. Now take backslashes into account, and that Python and friends escape those prior to using them. There are 16 backslashes in there, which adds another 16 characters. 27 | 28 | ```bash 29 | # Before 30 | \long\path 31 | 32 | # After 33 | \\long\\path 34 | ``` 35 | 36 | **204 characters** 37 | 38 | We still haven't changed the path, and yet the length has increased. Now take into account some libraries taking extra precautions and escapes even estaped backslashes. 39 | 40 | ```bash 41 | # Before 42 | \\long\\path 43 | 44 | # After 45 | \\\\long\\\\path 46 | ``` 47 | 48 | That adds yet another 32 characters. 49 | 50 | **236 characters** 51 | 52 | And again, we haven't changed our path, and yet this is what some tools will be working with, leaving you with very little room. 53 | 54 |
55 | 56 | ### Solution 57 | 58 | You've got at least three options here. 59 | 60 | 1. Patch your paths 61 | 1. Patch Rez 62 | 2. Patch Windows 63 | 64 | **Patch Paths** 65 | 66 | The most straightforward, but likely difficult, thing to do is to avoid long paths altogether. 67 | 68 | - Use a short hostname 69 | - Use a short repository path 70 | - Abbreviate Python libraries 71 | - Don't use Python packages from PyPI with long names 72 | 73 | But a lot of this is not practical, and merely postpones the issue. 74 | 75 | **Patch Rez** 76 | 77 | I've investigated what it would take to make changes to Rez that facilitate longer paths, and found that there is a prefix you can use for paths that will "force" Windows to interpret paths longer than 260 characters. 78 | 79 | ```bash 80 | # Before 81 | c:\long\path.exe 82 | 83 | # After 84 | \\?\c:\long\path.exe 85 | ``` 86 | 87 | Since paths are entirely managed by Rez, it wouldn't be unreasonable to wrap any path creation call to prefix the results with `\\?\` if the user was running Windows. But I couldn't find a single-point-of-entry for these, as paths were generated all over the place. Rightly so; it would be borderline overengineering to wrap all calls to e.g. `os.path.join` or `os.getcwd` into a "prefixer" just for this occasion. It would however have helped in this particular case. 88 | 89 | Furthermore, this would only really apply to Windows 10 and above, since from what I gather this (poorly documented) feature is only available there; possibly related to this next feature. 90 | 91 | **Patch Windows** 92 | 93 | You wouldn't think this is an option, but it just might be. 94 | 95 | This technically doesn't count as patching Windows, but because we're changing a fundamental component of the OS - something each applications has till now taken for granted - it may cause all sorts of havok for applications that depend on the 260 character limit. 96 | 97 | > Relevant comic https://xkcd.com/1172/ 98 | 99 | Since June 20th 2017, users of Windows 10 1607 have had the ability to enable support for "long paths". 100 | 101 | ```powershell 102 | # From an administrator PowerShell session 103 | Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem -Name LongPathsEnabled -Value 1 -Type DWord 104 | ``` 105 | 106 | This would effectively prepend `\\?\` to every path "under the hood", solving the issue. But at what cost? 107 | 108 | Let the community know if you encounter any issues by making [an issue](https://github.com/mottosso/allzpark/issues/new). 109 | 110 |
111 | 112 | ## Process Tree 113 | 114 | Virtualenv is one way of using Rez on Windows, and if you do then the `rez.exe` executable is generated during `pip install` and works by spawning a `python.exe` process, also generated by `pip`, which in turn calls on your system `python.exe`. Here's what spawning your own Python session from within a Rez context looks like. 115 | 116 | ![image](https://user-images.githubusercontent.com/2152766/59964221-e6b48780-94f5-11e9-8300-390d0587f5e3.png) 117 | 118 |
119 | 120 | ## Maya and Quicktime 121 | 122 | Typically, playblasting to `.mp4` or `.mov` with Maya requires a recent install of Quicktime on the local machine. Let's have a look at how to approach this with Rez. 123 | 124 | > How *does* one approach this with Rez? Submit [a PR](https://github.com/mottosso/allzpark) today! 125 | 126 |
127 | -------------------------------------------------------------------------------- /docs/theme/landing.css: -------------------------------------------------------------------------------- 1 | /* Styling specifically for the landing page */ 2 | 3 | .vboxlayout { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | .hboxlayout { 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-between; 12 | align-items: flex-start; 13 | } 14 | 15 | #pypiBadge { 16 | padding-top: 5px; 17 | } 18 | 19 | .vboxlayout p { 20 | margin-top: 0; 21 | font-size: 1.3em; 22 | font-weight: 300; 23 | } 24 | 25 | .row { 26 | flex-direction: row; 27 | } 28 | 29 | .column { 30 | flex-direction: column; 31 | } 32 | 33 | .column-reverse { 34 | flex-direction: column-reverse; 35 | } 36 | 37 | .row-reverse { 38 | flex-direction: row-reverse; 39 | } 40 | 41 | .justify-left { 42 | justify-content: flex-start; 43 | } 44 | 45 | .justify-right { 46 | justify-content: flex-end; 47 | } 48 | 49 | .justify-center { 50 | justify-content: center; 51 | } 52 | 53 | .flex-item { 54 | flex: 1; 55 | } 56 | 57 | .align-center { 58 | align-items: center; 59 | } 60 | 61 | .space { 62 | min-height: 100px; 63 | min-width: 100px; 64 | } 65 | 66 | .smallspace { 67 | min-height: 30px; 68 | min-width: 30px; 69 | } 70 | 71 | .auto-margin { 72 | margin: auto auto; 73 | } 74 | 75 | .stretch { 76 | flex-grow: 1; 77 | } 78 | 79 | .poster { 80 | box-shadow: 0 0 1.2rem rgba(0,0,0,.2), 0 0.2rem 0.4rem rgba(0,0,0,.2); 81 | border: 1px solid #777; 82 | } 83 | 84 | .md-typeset h2 { 85 | font-family: "Peinture Fraiche"; 86 | font-size: 4.0em; 87 | color: #222; 88 | margin-top: 0; 89 | line-height: 1em; 90 | } 91 | 92 | .landing { 93 | display: none; 94 | } 95 | 96 | #title { 97 | font-family: "Peinture Fraiche"; 98 | font-size: 6.0em; 99 | color: #222; 100 | margin-bottom: 0; 101 | } 102 | 103 | #landing { 104 | align-items: center; 105 | } 106 | 107 | #description { 108 | font-size: 1em; 109 | max-width: 420px; 110 | padding-right: 50px; 111 | } 112 | 113 | #conclusion { 114 | text-align: center; 115 | } 116 | 117 | [data-md-color-primary] .md-typeset a.button { 118 | font-family: "Roboto", Corbel, Avenir, "Lucida Grande", "Lucida Sans", sans-serif; 119 | max-width: 400px; 120 | min-width: 120px; 121 | -webkit-appearance: none; 122 | -moz-appearance: none; 123 | display: block; 124 | border-radius: 3px; 125 | margin: 5px; 126 | margin-left: 0; 127 | margin-right: 10px; 128 | padding: 15px; 129 | text-align: center; 130 | text-decoration: none; 131 | font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; 132 | color: #eee; 133 | transition: background 1s cubic-bezier(0.1, 0.7, 0.1, 1); 134 | } 135 | 136 | /* Little pen icon, to edit the page*/ 137 | .md-content__icon { 138 | display: none; 139 | } 140 | 141 | .button:active { 142 | position: relative; 143 | top: 6px; 144 | } 145 | 146 | .green { 147 | background: #5aa926; 148 | box-shadow: 0px 6px #418624; 149 | } 150 | 151 | .green:active { 152 | background: #79c15c; 153 | box-shadow: 0px 0px #79c15c; 154 | } 155 | 156 | .green:hover { 157 | background: #79c15c; 158 | } 159 | 160 | .blue { 161 | background: #3498db; 162 | box-shadow: 0px 6px #2980b9; 163 | } 164 | 165 | .blue:active { 166 | background: #2980b9; 167 | box-shadow: 0px 0px #2980b9; 168 | } 169 | 170 | .blue:hover { 171 | background: #4BA4DE; 172 | } 173 | 174 | .red { 175 | box-shadow: 0px 6px #c0392b; 176 | background: #e74c3c; 177 | } 178 | 179 | .red:active { 180 | box-shadow: 0px 0px #c0392b; 181 | background: #c0392b; 182 | } 183 | 184 | .red:hover { 185 | background: #E55E50; 186 | position: relative; 187 | } 188 | 189 | .md-typeset a { 190 | color: #0070e6; 191 | font-weight: 400; 192 | } 193 | 194 | .md-typeset .codehilite, .md-typeset .highlight { 195 | min-width: 50%; 196 | } 197 | 198 | .codehilite code, .md-typeset .codehilite pre, .md-typeset .highlight code, .md-typeset .highlight pre { 199 | font-family: "Source Code Pro"; 200 | font-size: 1.11em; 201 | } 202 | 203 | /* Smaller width */ 204 | @media only screen and (max-width: 1219px) { 205 | .landing { 206 | display: inherit; 207 | } 208 | } 209 | 210 | /* Mobile */ 211 | @media only screen and (max-width: 60em) { 212 | .hboxlayout { 213 | flex-direction: column; 214 | justify-content: center; 215 | align-content: start; 216 | align-items: center; 217 | } 218 | 219 | .vboxlayout { 220 | align-items: center; 221 | } 222 | 223 | .vboxlayout p { 224 | width: 75%; 225 | } 226 | 227 | .md-typeset img { 228 | margin: 0 auto; 229 | } 230 | 231 | [data-md-color-primary] .md-typeset a.button { 232 | margin: 5px auto; 233 | } 234 | 235 | #description { 236 | padding: 0; 237 | width: inherit; 238 | } 239 | 240 | .container { 241 | margin: 0px auto; 242 | padding-top: 20px; 243 | text-align: center; 244 | } 245 | 246 | h2 { 247 | padding-top: 100px; 248 | text-align: center; 249 | } 250 | 251 | p { 252 | text-align: center; 253 | } 254 | 255 | .space { 256 | min-height: 0px; 257 | } 258 | } -------------------------------------------------------------------------------- /tests/test_profiles.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import mock 3 | from tests import util 4 | 5 | 6 | class TestProfiles(util.TestBase): 7 | 8 | def test_reset(self): 9 | """Test session reset 10 | """ 11 | util.memory_repository({ 12 | "foo": { 13 | "1.0.0": { 14 | "name": "foo", 15 | "version": "1.0.0", 16 | } 17 | }, 18 | "bar": { 19 | "1.0.0": { 20 | "name": "bar", 21 | "version": "1.0.0", 22 | } 23 | } 24 | }) 25 | with self.wait_signal(self.ctrl.resetted): 26 | self.ctrl.reset(["foo", "bar"]) 27 | self.wait(timeout=200) 28 | self.assertEqual(self.ctrl.state.state, "noapps") 29 | 30 | # last profile will be selected by default 31 | self.assertEqual("bar", self.ctrl.state["profileName"]) 32 | self.assertEqual(["foo", "bar"], list(self.ctrl.state["rezProfiles"])) 33 | 34 | def test_select_profile_with_out_apps(self): 35 | """Test selecting profile that has no apps 36 | """ 37 | util.memory_repository({ 38 | "foo": { 39 | "1.0.0": { 40 | "name": "foo", 41 | "version": "1.0.0", 42 | } 43 | }, 44 | "bar": { 45 | "1.0.0": { 46 | "name": "bar", 47 | "version": "1.0.0", 48 | } 49 | } 50 | }) 51 | with self.wait_signal(self.ctrl.resetted): 52 | self.ctrl.reset(["foo", "bar"]) 53 | self.wait(timeout=200) 54 | self.assertEqual(self.ctrl.state.state, "noapps") 55 | 56 | with self.wait_signal(self.ctrl.state_changed, "noapps"): 57 | self.ctrl.select_profile("foo") 58 | # wait enter 'noapps' state 59 | 60 | self.assertEqual("foo", self.ctrl.state["profileName"]) 61 | 62 | def test_profile_list_apps(self): 63 | """Test listing apps from profile 64 | """ 65 | util.memory_repository({ 66 | "foo": { 67 | "1.0.0": { 68 | "name": "foo", 69 | "version": "1.0.0", 70 | "requires": [ 71 | "lib_foo", 72 | "~app_A", 73 | "~app_B", 74 | ], 75 | } 76 | }, 77 | "app_A": { 78 | "1.0.0": { 79 | "name": "app_A", 80 | "version": "1.0.0", 81 | } 82 | }, 83 | "app_B": { 84 | "1.0.0": { 85 | "name": "app_B", 86 | "version": "1.0.0", 87 | } 88 | }, 89 | "lib_foo": { 90 | "1.0.0": { 91 | "name": "lib_foo", 92 | "version": "1.0.0", 93 | } 94 | }, 95 | }) 96 | self.ctrl_reset(["foo"]) 97 | 98 | with self.wait_signal(self.ctrl.state_changed, "ready"): 99 | self.ctrl.select_profile("foo") 100 | 101 | self.assertEqual( 102 | [ 103 | "app_A==1.0.0", 104 | "app_B==1.0.0", 105 | ], 106 | list(self.ctrl.state["rezApps"].keys()) 107 | ) 108 | 109 | def test_profile_listing_without_root_err(self): 110 | """Listing profile without root will raise AssertionError""" 111 | self.assertRaises(AssertionError, self.ctrl.reset) 112 | self.assertRaises(AssertionError, self.ctrl.list_profiles) 113 | 114 | def test_profile_listing_callable_root_err(self): 115 | """Listing profile with bad callable will prompt error message""" 116 | import traceback 117 | import logging 118 | from allzpark import control 119 | 120 | traceback.print_exc = mock.MagicMock(name="traceback.print_exc") 121 | self.ctrl.error = mock.MagicMock(name="Controller.error") 122 | 123 | def bad_root(): 124 | raise Exception("This should be caught.") 125 | self.ctrl.list_profiles(bad_root) 126 | 127 | # ctrl.error must be called in all cases 128 | self.ctrl.error.assert_called_once() 129 | # traceback.print_exc should be called if logging level is set 130 | # lower than INFO, e.g. DEBUG or NOTSET 131 | if control.log.level < logging.INFO: 132 | traceback.print_exc.assert_called_once() 133 | 134 | def test_profile_listing_invalid_type_root_err(self): 135 | """Listing profile with invalid input type will raise TypeError""" 136 | self.assertRaises(TypeError, self.ctrl.list_profiles, {"foo"}) 137 | 138 | def test_profile_listing_filter_out_empty_names(self): 139 | """Listing profile with empty names will be filtered""" 140 | expected = ["foo", "bar"] 141 | profiles = self.ctrl.list_profiles(expected + [None, ""]) 142 | self.assertEqual(profiles, expected) 143 | -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | import unittest 5 | import contextlib 6 | 7 | 8 | MEMORY_LOCATION = "memory@any" 9 | 10 | 11 | def memory_repository(packages): 12 | from rezplugins.package_repository import memory 13 | from allzpark import _rezapi as rez 14 | 15 | class MemoryVariantRes(memory.MemoryVariantResource): 16 | def _root(self): # implement `root` to work with localz 17 | return MEMORY_LOCATION 18 | 19 | manager = rez.package_repository_manager 20 | repository = manager.get_repository(MEMORY_LOCATION) 21 | repository.pool.resource_classes[MemoryVariantRes.key] = MemoryVariantRes 22 | repository.data = packages 23 | 24 | 25 | class TestBase(unittest.TestCase): 26 | 27 | def setUp(self): 28 | from allzpark import cli 29 | 30 | os.environ["ALLZPARK_PREFERENCES_NAME"] = "preferences_test" 31 | os.environ["REZ_PACKAGES_PATH"] = MEMORY_LOCATION 32 | 33 | app, ctrl = cli.initialize(clean=True, verbose=3) 34 | window = cli.launch(ctrl) 35 | 36 | size = window.size() 37 | window.resize(size.width() + 80, size.height() + 80) 38 | 39 | self.app = app 40 | self.ctrl = ctrl 41 | self.window = window 42 | self.patched_allzparkconfig = dict() 43 | 44 | self.wait(timeout=50) 45 | 46 | def tearDown(self): 47 | self.wait(timeout=500) 48 | self.window.close() 49 | self.ctrl.deleteLater() 50 | self.window.deleteLater() 51 | self._restore_allzparkconfig() 52 | time.sleep(0.1) 53 | 54 | def _restore_allzparkconfig(self): 55 | from allzpark import allzparkconfig 56 | 57 | for name, value in self.patched_allzparkconfig.items(): 58 | setattr(allzparkconfig, name, value) 59 | 60 | self.patched_allzparkconfig.clear() 61 | 62 | def patch_allzparkconfig(self, name, value): 63 | from allzpark import allzparkconfig 64 | 65 | if name not in self.patched_allzparkconfig: 66 | original = getattr(allzparkconfig, name) 67 | self.patched_allzparkconfig[name] = original 68 | 69 | setattr(allzparkconfig, name, value) 70 | 71 | def set_preference(self, name, value): 72 | preferences = self.window._docks["preferences"] 73 | arg = next((opt for opt in preferences.options 74 | if opt["name"] == name), None) 75 | if not arg: 76 | self.fail("Preference doesn't have this setting: %s" % name) 77 | 78 | try: 79 | arg.write(value) 80 | except Exception as e: 81 | self.fail("Preference '%s' set failed: %s" % (name, str(e))) 82 | 83 | def show_dock(self, name, on_page=None): 84 | dock = self.window._docks[name] 85 | dock.toggle.setChecked(True) 86 | dock.toggle.clicked.emit() 87 | self.wait(timeout=50) 88 | 89 | if on_page is not None: 90 | tabs = dock._panels["central"] 91 | page = dock._pages[on_page] 92 | index = tabs.indexOf(page) 93 | tabs.tabBar().setCurrentIndex(index) 94 | 95 | return dock 96 | 97 | def ctrl_reset(self, profiles): 98 | with self.wait_signal(self.ctrl.resetted): 99 | self.ctrl.reset(profiles) 100 | self.wait(timeout=200) 101 | self.assertEqual(self.ctrl.state.state, "ready") 102 | 103 | def select_application(self, app_request): 104 | apps = self.window._widgets["apps"] 105 | proxy = apps.model() 106 | model = proxy.sourceModel() 107 | index = model.findIndex(app_request) 108 | index = proxy.mapFromSource(index) 109 | 110 | sel_model = apps.selectionModel() 111 | sel_model.select(index, sel_model.ClearAndSelect | sel_model.Rows) 112 | self.wait(50) 113 | 114 | def wait(self, timeout=1000): 115 | from allzpark.vendor.Qt import QtCore 116 | 117 | loop = QtCore.QEventLoop(self.window) 118 | timer = QtCore.QTimer(self.window) 119 | 120 | def on_timeout(): 121 | timer.stop() 122 | loop.quit() 123 | 124 | timer.timeout.connect(on_timeout) 125 | timer.start(timeout) 126 | loop.exec_() 127 | 128 | @contextlib.contextmanager 129 | def wait_signal(self, signal, on_value=None, timeout=1000): 130 | from allzpark.vendor.Qt import QtCore 131 | 132 | loop = QtCore.QEventLoop(self.window) 133 | timer = QtCore.QTimer(self.window) 134 | state = {"received": False} 135 | 136 | if on_value is None: 137 | def trigger(*args): 138 | state["received"] = True 139 | timer.stop() 140 | loop.quit() 141 | else: 142 | def trigger(value): 143 | if value == on_value: 144 | state["received"] = True 145 | timer.stop() 146 | loop.quit() 147 | 148 | def on_timeout(): 149 | timer.stop() 150 | loop.quit() 151 | self.fail("Signal waiting timeout.") 152 | 153 | signal.connect(trigger) 154 | timer.timeout.connect(on_timeout) 155 | 156 | try: 157 | yield 158 | finally: 159 | if not state["received"]: 160 | timer.start(timeout) 161 | loop.exec_() 162 | 163 | def get_menu(self, widget): 164 | from allzpark.vendor.Qt import QtWidgets 165 | menus = widget.findChildren(QtWidgets.QMenu, "") 166 | menu = next((m for m in menus if m.isVisible()), None) 167 | if menu: 168 | return menu 169 | else: 170 | self.fail("This widget doesn't have menu.") 171 | 172 | 173 | @contextlib.contextmanager 174 | def patch_cursor_pos(point): 175 | from allzpark.vendor.Qt import QtGui 176 | 177 | origin_pos = getattr(QtGui.QCursor, "pos") 178 | setattr(QtGui.QCursor, "pos", lambda: point) 179 | try: 180 | yield 181 | finally: 182 | setattr(QtGui.QCursor, "pos", origin_pos) 183 | -------------------------------------------------------------------------------- /allzpark/allzparkconfig.py: -------------------------------------------------------------------------------- 1 | """The Allzpark configuration file 2 | 3 | Copy this onto your local drive and make modifications. 4 | Anything not specified in your copy is inherited from here. 5 | 6 | ALLZPARK_CONFIG_FILE=/path/to/allzparkconfig.py 7 | 8 | """ 9 | 10 | import os as __os 11 | 12 | 13 | # Load this profile on startup. 14 | # Defaults to the first available from `profiles` 15 | startup_profile = "" # (optional) 16 | 17 | # Pre-select this application in the list of applications, 18 | # if it exists in the startup profile. 19 | startup_application = "" # (optional) 20 | 21 | # Default filter, editable via the Preferences page 22 | exclude_filter = "*.beta" 23 | 24 | # Where to go when clicking the logo 25 | help_url = "https://allzpark.com" 26 | 27 | 28 | def profiles(): 29 | """Return list of profiles 30 | 31 | This function is called asynchronously, and is suitable 32 | for making complex filesystem or database queries. 33 | Can also be a variable of type tuple or list 34 | 35 | """ 36 | 37 | try: 38 | return __os.listdir(__os.path.expanduser("~/profiles")) 39 | except IOError: 40 | return [] 41 | 42 | 43 | def applications(): 44 | """Return list of applications 45 | 46 | Applications are typically provided by the profile, 47 | this function is called when "Show all apps" is enabled. 48 | 49 | """ 50 | 51 | return [] 52 | 53 | 54 | def applications_from_package(variant): 55 | """Return applications relative `variant` 56 | 57 | Returns: 58 | list of strings: E.g. ['appA', 'appB==2019'] 59 | 60 | """ 61 | 62 | from . import _rezapi as rez 63 | 64 | # May not be defined 65 | requirements = variant.requires or [] 66 | 67 | apps = list( 68 | str(req) 69 | for req in requirements 70 | if req.weak 71 | ) 72 | 73 | return apps 74 | 75 | 76 | def metadata_from_package(variant): 77 | """Return metadata relative `variant` 78 | 79 | Blocking call, during change of profile. 80 | 81 | IMPORTANT: this function must return at least the 82 | members part of the original function, else the program 83 | will not function. Very few safeguards are put in place 84 | in favour of performance. 85 | 86 | Arguments: 87 | variant (rez.packages_.Variant): Package from which to retrieve data 88 | 89 | Returns: 90 | dict: See function for values and types 91 | 92 | """ 93 | 94 | data = getattr(variant, "_data", {}) 95 | 96 | return dict(data, **{ 97 | 98 | # Guaranteed keys, with default values 99 | "label": data.get("label", variant.name), 100 | "background": data.get("background"), 101 | "icon": data.get("icon", ""), 102 | "hidden": data.get("hidden", False), 103 | }) 104 | 105 | 106 | def protected_preferences(): 107 | """Protect preference settings 108 | 109 | Prevent clueless one from touching danger settings. 110 | 111 | Following is a list of preference names that you may lock: 112 | * showAllApps (bool) 113 | * showHiddenApps (bool) 114 | * showAllVersions (bool) 115 | * patchWithFilter (bool) 116 | * clearCacheTimeout (int) 117 | * exclusionFilter (str) 118 | 119 | This should return a preference name and default value paired 120 | dict. For example: {"showAllVersions": False} 121 | 122 | Returns: 123 | dict 124 | 125 | """ 126 | return dict() 127 | 128 | 129 | def themes(): 130 | """Allzpark GUI theme list provider 131 | 132 | This will only be called once on startup. 133 | 134 | Each theme in list is a dict object, for example: 135 | 136 | { 137 | "name": "theme_name", 138 | "source": "my_style.css", 139 | "keywords": {"base-tone": "red", "res": "path-to-icons"}, 140 | } 141 | 142 | * `name` is the theme name, this is required. 143 | * `source` can be a file path or plain css code, this is required. 144 | * `keywords` is optional, must be dict type if provided, will be 145 | used to string format the css code. 146 | 147 | Returns: 148 | list 149 | 150 | """ 151 | return [] 152 | 153 | 154 | def application_parent_environment(): 155 | """Application's launching environment 156 | 157 | You may want to set this so the application won't be inheriting current 158 | environment which is used to launch Allzpark. E.g. when Allzaprk is 159 | launched from a Rez resolved context. 160 | 161 | But if using bleeding-rez, and `config.inherit_parent_environment` is 162 | set to False, config will be respected and this will be ignored. 163 | 164 | Returns: 165 | dict 166 | 167 | """ 168 | return None 169 | 170 | 171 | def subprocess_encoding(): 172 | """Codec that should be used to decode subprocess stdout/stderr 173 | 174 | See https://docs.python.org/3/library/codecs.html#standard-encodings 175 | 176 | Returns: 177 | str: name of codec 178 | 179 | """ 180 | # nerdvegas/rez sets `encoding='utf-8'` when `universal_newlines=True` and 181 | # `encoding` is not in Popen kwarg. 182 | return "utf-8" 183 | 184 | 185 | def unicode_decode_error_handler(): 186 | """Error handler for handling UnicodeDecodeError in subprocess 187 | 188 | See https://docs.python.org/3/library/codecs.html#error-handlers 189 | 190 | Returns: 191 | str: name of registered error handler 192 | 193 | """ 194 | import codecs 195 | import locale 196 | 197 | def decode_with_preferred_encoding(exception): 198 | encoding = locale.getpreferredencoding(do_setlocale=False) 199 | invalid_bytes = exception.object[exception.start:] 200 | 201 | text = invalid_bytes.decode(encoding, 202 | # second fallback 203 | errors="backslashreplace") 204 | 205 | return text, len(exception.object) 206 | 207 | handler_name = "decode_with_preferred_encoding" 208 | try: 209 | codecs.lookup_error(handler_name) 210 | except LookupError: 211 | codecs.register_error(handler_name, decode_with_preferred_encoding) 212 | 213 | return handler_name 214 | -------------------------------------------------------------------------------- /allzpark/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | import traceback 5 | import functools 6 | import contextlib 7 | import logging 8 | import collections 9 | import webbrowser 10 | import subprocess 11 | 12 | from .vendor import six 13 | from .vendor.Qt import QtCore 14 | 15 | _lru_cache = {} 16 | _threads = [] 17 | _basestring = six.string_types[0] # For Python 2/3 18 | _log = logging.getLogger(__name__) 19 | _timer = (time.process_time 20 | if six.PY3 else (time.time if os.name == "nt" else time.clock)) 21 | 22 | USE_THREADING = not bool(os.getenv("ALLZPARK_NOTHREADING")) 23 | 24 | 25 | @contextlib.contextmanager 26 | def timing(): 27 | t0 = _timer() 28 | result = type("timing", (object,), {"duration": None}) 29 | try: 30 | yield result 31 | finally: 32 | t1 = _timer() 33 | result.duration = t1 - t0 34 | 35 | 36 | def delay(func, delay=50): 37 | """Postpone `func` by `delay` milliseconds 38 | 39 | This is used to allow Qt to finish rendering prior 40 | to occupying the main thread. Such as calling some 41 | CPU-heavy function on a `QPushButton.pressed` event, 42 | which would normally freeze the GUI without letting 43 | the button unclick itself, resulting in unexpected 44 | visual artifacts. 45 | 46 | """ 47 | 48 | QtCore.QTimer.singleShot(delay, func) 49 | 50 | 51 | def async_(func): 52 | """No-op decorator, used to visually distinguish async_ functions""" 53 | return func 54 | 55 | 56 | def cached(func): 57 | """Cache returnvalue of `func`""" 58 | 59 | @functools.wraps(func) 60 | def wrapper(*args, **kwargs): 61 | key = "{func}:{args}:{kwargs}".format( 62 | func=func.__name__, 63 | args=", ".join(str(arg) for arg in args), 64 | kwargs=", ".join( 65 | "%s=%s" % (key, value) 66 | for key, value in kwargs.items() 67 | ) 68 | ) 69 | 70 | try: 71 | value = _lru_cache[key] 72 | except KeyError: 73 | value = func(*args, **kwargs) 74 | _lru_cache[key] = value 75 | 76 | return value 77 | return wrapper 78 | 79 | 80 | def windows_taskbar_compat(): 81 | """Enable icon and taskbar grouping for Windows 7+""" 82 | 83 | import ctypes 84 | ctypes.windll.shell32.\ 85 | SetCurrentProcessExplicitAppUserModelID( 86 | u"allzpark") 87 | 88 | 89 | if USE_THREADING: 90 | def defer(target, 91 | args=None, 92 | kwargs=None, 93 | on_success=lambda object: None, 94 | on_failure=lambda exception: None): 95 | """Perform operation in thread with callback 96 | 97 | Arguments: 98 | target (callable): Method or function to call 99 | callback (callable, optional): Method or function to call 100 | once `target` has finished. 101 | 102 | Returns: 103 | None 104 | 105 | """ 106 | 107 | thread = Thread(target, args, kwargs, on_success, on_failure) 108 | thread.finished.connect(lambda: _threads.remove(thread)) 109 | thread.start() 110 | 111 | # Cache until finished 112 | # If we didn't do this, Python steps in to garbage 113 | # collect the thread before having had time to finish, 114 | # resulting in an exception. 115 | _threads.append(thread) 116 | 117 | return thread 118 | 119 | else: 120 | # Debug mode, execute "threads" immediately on the main thread 121 | _log.warning("Threading disabled") 122 | 123 | def defer(target, 124 | args=None, 125 | kwargs=None, 126 | on_success=lambda object: None, 127 | on_failure=lambda exception: None): 128 | try: 129 | result = target(*(args or []), **(kwargs or {})) 130 | except Exception as e: 131 | error = traceback.format_exc() 132 | on_failure(e, error) 133 | else: 134 | on_success(result) 135 | 136 | 137 | class Thread(QtCore.QThread): 138 | succeeded = QtCore.Signal(object) 139 | failed = QtCore.Signal(Exception, _basestring) 140 | 141 | def __init__(self, 142 | target, 143 | args=None, 144 | kwargs=None, 145 | on_success=None, 146 | on_failure=None): 147 | super(Thread, self).__init__() 148 | 149 | self.args = args or list() 150 | self.kwargs = kwargs or dict() 151 | self.target = target 152 | self.on_success = on_success 153 | self.on_failure = on_failure 154 | 155 | connection = QtCore.Qt.BlockingQueuedConnection 156 | 157 | if on_success is not None: 158 | self.succeeded.connect(self.on_success, type=connection) 159 | 160 | if on_failure is not None: 161 | self.failed.connect(self.on_failure, type=connection) 162 | 163 | def run(self, *args, **kwargs): 164 | try: 165 | result = self.target(*self.args, **self.kwargs) 166 | 167 | except Exception as e: 168 | error = traceback.format_exc() 169 | return self.failed.emit(e, error) 170 | 171 | else: 172 | self.succeeded.emit(result) 173 | 174 | 175 | def iterable(arg): 176 | return ( 177 | isinstance(arg, collections.Iterable) 178 | and not isinstance(arg, six.string_types) 179 | ) 180 | 181 | 182 | def open_file_location(fname): 183 | if os.path.exists(fname): 184 | if os.name == "nt": 185 | subprocess.Popen("explorer /select,%s" % fname) 186 | else: 187 | webbrowser.open(os.path.dirname(fname)) 188 | else: 189 | raise OSError("%s did not exist" % fname) 190 | 191 | 192 | def normpath(path): 193 | return os.path.normpath( 194 | os.path.normcase(os.path.abspath(path)).replace("\\", "/") 195 | ) 196 | 197 | 198 | def normpaths(*paths): 199 | return list(map(normpath, paths)) 200 | 201 | 202 | def atoi(text): 203 | return int(text) if text.isdigit() else text 204 | 205 | 206 | def natural_keys(text): 207 | """Key for use with sorted(key=) and str.sort(key=) 208 | 209 | alist.sort(key=natural_keys) sorts in human order 210 | http://nedbatchelder.com/blog/200712/human_sorting.html 211 | (See Toothy's implementation in the comments) 212 | 213 | """ 214 | 215 | return [atoi(c) for c in re.split(r'(\d+)', text)] 216 | -------------------------------------------------------------------------------- /docs/pages/contributing.md: -------------------------------------------------------------------------------- 1 | Thanks for considering making a contribution to the Allzpark project! 2 | 3 | The goal of Allzpark is making film and games productions more fun to work on, for artists and developers alike. Almost every company with any experience working in this field has felt the pain of managing software and versions when all you really want to do is make great pictures. Allzpark can really help with that, and you can really help Allzpark! 4 | 5 |
6 | 7 | ### Quickstart 8 | 9 | Allzpark works out of the Git repository. 10 | 11 | ```bash 12 | git clone https://github.com/mottosso/allzpark.git 13 | cd allzpark 14 | python -m allzpark --demo 15 | ``` 16 | 17 | Get the up-to-date requirements by having a copy of Allzpark already installed. 18 | 19 | - See [Quickstart](/quickstart) for details 20 | 21 |
22 | 23 | ### Architecture 24 | 25 | The front-end is written in Python and [Qt.py](https://github.com/mottosso/Qt.py), and the back-end is [bleeding-rez](https://github.com/mottosso/bleeding-rez). You are welcome to contribute to either of these projects. 26 | 27 | Graphically, the interface is written in standard Qt idioms, like MVC to separate between logic and visuals. The window itself is an instance of `QMainWindow`, whereby each "tab" is a regular `QDockWidget`, which is how you can move them around and dock them freely. 28 | 29 | - [model.py](https://github.com/mottosso/allzpark/blob/master/allzpark/model.py) 30 | - [view.py](https://github.com/mottosso/allzpark/blob/master/allzpark/view.py) 31 | - [control.py](https://github.com/mottosso/allzpark/blob/master/allzpark/control.py) 32 | 33 | User preferences is stored in a `QSettings` object, including window layout. See `view.py:Window.closeEvent()` for how that works. 34 | 35 |
36 | 37 | ### Development 38 | 39 | To make changes and/or contribute to Allzpark, here's how to run it from its Git repository. 40 | 41 | ```bash 42 | git clone https://github.com/mottosso/allzpark.git 43 | cd allzpark 44 | python -m allzpark 45 | ``` 46 | 47 | From here, Python picks up the `allzpark` package from the current working directory, and everything is set to go. For use with Rez, try this. 48 | 49 | ```bash 50 | # powershell 51 | git clone https://github.com/mottosso/allzpark.git 52 | cd allzpark 53 | . env.ps1 54 | > python -m allzpark 55 | ``` 56 | 57 | This will ensure a reproducible environment via Rez packages. 58 | 59 |
60 | 61 | ### Versioning 62 | 63 | You typically won't have to manually increment the version of this project. 64 | 65 | Instead, you can find the current version based on the current commit. 66 | 67 | ```bash 68 | python -m allzpark --version 69 | 1.3.5 70 | ``` 71 | 72 | This is the version to be used when making a new GitHub release, and the version used by setup.py during release on PyPI (in case you should accidentally tag your GitHub release errouneously). 73 | 74 | Major and minor versions are incremented for breaking and new changes respectively, the patch version however is special. It is incremented automatically in correspondance with the current commit number. E.g. commit number 200 yields a patch number of 200. See `allzpark/version.py` for details. 75 | 76 | To see the patch version as you develop, ensure `git` is available on PATH, as it is used to detect the commit number at launch. Once built and distributed to PyPI, this number is then embedded into the resulting package. See `setup.py` for details. 77 | 78 |
79 | 80 | ### Resources 81 | 82 | The current icon set is from [Haiku](https://github.com/darealshinji/haiku-icons). 83 | 84 |
85 | 86 | ### Guidelines 87 | 88 | There are a few ways you can contribute to this project. 89 | 90 | 1. Use it and report any issues [here](https://github.com/mottosso/allzpark/issues) 91 | 1. Submit ideas for improvements or new features [here](https://github.com/mottosso/allzpark/issues) 92 | 1. Add or improve [this documentation](https://github.com/mottosso/allzpark/tree/master/docs) 93 | 1. Help write tests to avoid regressions and help future contributors spot mistakes 94 | 95 | Any other thoughts on how you would like to contribute? [Let me know](https://github.com/mottosso/allzpark/issues). 96 | 97 |
98 | 99 | ### Documentation 100 | 101 | The documentation you are reading right now is hosted in the Allzpark git repository on GitHub, and built with a static site-generator called [mkdocs](https://www.mkdocs.org/) along with a theme called [mkdocs-material](https://squidfunk.github.io/mkdocs-material/). 102 | 103 | Mkdocs can host the entirety of the website on your local machine, and automatically update whenever you make changes to the Markdown documents. Here's how you can get started. 104 | 105 |
106 | 107 | 108 |
109 | 110 |
111 | 112 | You can either use Rez and Pipz. 113 | 114 | ```powershell 115 | cd allzpark\docs 116 | rez env pipz -- install -r requirements.txt 117 | . serve.ps1 118 | ``` 119 | 120 | Or install dependencies into your system-wide Python. 121 | 122 | ```powershell 123 | cd allzpark\docs 124 | pip install -r requirements.txt 125 | mkdocs serve 126 | ``` 127 | 128 |
129 | 130 |
131 | 132 | ```bash 133 | cd allzpark/docs 134 | rez env pipz -- install -r requirements.txt 135 | rez env git python mkdocs_material-4.4.0 mkdocs_git_revision_date_plugin==0.1.5 -- mkdocs serve 136 | ``` 137 | 138 | Or install dependencies into your system-wide Python. 139 | 140 | ```powershell 141 | cd allzpark/docs 142 | pip install -r requirements.txt 143 | mkdocs serve 144 | ``` 145 | 146 |
147 | 148 | You should see a message about how to browse to the locally hosted documentation in your console. 149 | 150 |
151 | 152 | #### Guidelines 153 | 154 | Things to keep in mind as you contribute to the documentation 155 | 156 | - **Windows-first** Allzpark is for all platforms, but Windows-users are typically less tech-savvy than Linux and MacOS users and the documentation should reflect that. 157 | - **Try-catch** When documenting a series of steps to accomplish a task, start with the minimal ideal case, and then "catch" potential errors afterwards. This helps keep the documentation from branching out too far, and facilitates a cursory skimming of the documentation. See [quickstart](/quickstart) for an example. 158 | -------------------------------------------------------------------------------- /allzpark/resources.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from collections import OrderedDict as odict 4 | from . import allzparkconfig 5 | from .vendor.Qt import QtGui 6 | 7 | dirname = os.path.dirname(__file__) 8 | _cache = {} 9 | _themes = odict() 10 | 11 | 12 | def px(value, scale=1.0): 13 | return int(value * scale) 14 | 15 | 16 | def find(*paths): 17 | fname = os.path.join(dirname, "resources", *paths) 18 | fname = os.path.normpath(fname) # Conform slashes and backslashes 19 | return fname.replace("\\", "/") # Cross-platform compatibility 20 | 21 | 22 | def pixmap(*paths): 23 | path = find(*paths) 24 | basename = paths[-1] 25 | name, ext = os.path.splitext(basename) 26 | 27 | if not ext: 28 | path += ".png" 29 | 30 | try: 31 | pixmap = _cache[paths] 32 | except KeyError: 33 | pixmap = QtGui.QPixmap(find(*paths)) 34 | _cache[paths] = pixmap 35 | 36 | return pixmap 37 | 38 | 39 | def icon(*paths): 40 | return QtGui.QIcon(pixmap(*paths)) 41 | 42 | 43 | def load_themes(): 44 | _themes.clear() 45 | for theme in default_themes() + allzparkconfig.themes(): 46 | _themes[theme["name"]] = theme 47 | 48 | 49 | def theme_names(): 50 | for name in _themes.keys(): 51 | yield name 52 | 53 | 54 | def load_theme(name=None): 55 | if name: 56 | theme = _themes.get(name) 57 | if theme is None: 58 | print("No theme named: %s" % name) 59 | return 60 | else: 61 | theme = next(iter(_themes.values())) 62 | 63 | source = theme["source"] 64 | keywords = theme.get("keywords", dict()) 65 | 66 | if any(source.endswith(ext) for ext in [".css", ".qss"]): 67 | if not os.path.isfile(source): 68 | print("Theme stylesheet file not found: %s" % source) 69 | return 70 | else: 71 | with open(source) as f: 72 | css = f.read() 73 | else: 74 | # plain css code 75 | css = source 76 | 77 | _cache["_keywordsCache_"] = keywords 78 | _cache["_logColorCache_"] = { 79 | logging.DEBUG: keywords.get("log.debug", "lightgrey"), 80 | logging.INFO: keywords.get("log.info", "grey"), 81 | logging.WARNING: keywords.get("log.warning", "darkorange"), 82 | logging.ERROR: keywords.get("log.error", "lightcoral"), 83 | logging.CRITICAL: keywords.get("log.critical", "red"), 84 | } 85 | 86 | return format_stylesheet(css) 87 | 88 | 89 | def format_stylesheet(css): 90 | try: 91 | return css % _cache["_keywordsCache_"] 92 | except KeyError as e: 93 | print("Stylesheet format failed: %s" % str(e)) 94 | return "" 95 | 96 | 97 | def log_level_color(level): 98 | log_colors = _cache.get("_logColorCache_", dict()) 99 | return log_colors.get(level, "grey") 100 | 101 | 102 | def default_themes(): 103 | _load_fonts() 104 | res_root = os.path.join(dirname, "resources").replace("\\", "/") 105 | return [ 106 | { 107 | "name": "default-dark", 108 | "source": find("style-dark.css"), 109 | "keywords": { 110 | "prim": "#2E2C2C", 111 | "brightest": "#403E3D", 112 | "bright": "#383635", 113 | "base": "#2E2C2C", 114 | "dim": "#21201F", 115 | "dimmest": "#141413", 116 | "hover": "rgba(104, 182, 237, 60)", 117 | "highlight": "rgb(110, 191, 245)", 118 | "highlighted": "#111111", 119 | "active": "silver", 120 | "inactive": "dimGray", 121 | "console": "#161616", 122 | "log.debug": "lightgrey", 123 | "log.info": "grey", 124 | "log.warning": "darkorange", 125 | "log.error": "lightcoral", 126 | "log.critical": "red", 127 | "res": res_root, 128 | } 129 | }, 130 | { 131 | "name": "default-light", 132 | "source": find("style-light.css"), 133 | "keywords": { 134 | "prim": "#FFFFFF", 135 | "brightest": "#FDFDFD", 136 | "bright": "#F9F9F9", 137 | "base": "#EFEFEF", 138 | "dim": "#DFDFDF", 139 | "dimmest": "#CFCFCF", 140 | "hover": "rgba(122, 194, 245, 60)", 141 | "highlight": "rgb(136, 194, 235)", 142 | "highlighted": "#111111", 143 | "active": "black", 144 | "inactive": "gray", 145 | "console": "#363636", 146 | "log.debug": "lightgrey", 147 | "log.info": "grey", 148 | "log.warning": "darkorange", 149 | "log.error": "lightcoral", 150 | "log.critical": "red", 151 | "res": res_root, 152 | } 153 | }, 154 | ] 155 | 156 | 157 | def _load_fonts(): 158 | """Load default fonts from resources""" 159 | _res_root = os.path.join(dirname, "resources").replace("\\", "/") 160 | 161 | font_root = os.path.join(_res_root, "fonts") 162 | fonts = [ 163 | "opensans/OpenSans-Bold.ttf", 164 | "opensans/OpenSans-BoldItalic.ttf", 165 | "opensans/OpenSans-ExtraBold.ttf", 166 | "opensans/OpenSans-ExtraBoldItalic.ttf", 167 | "opensans/OpenSans-Italic.ttf", 168 | "opensans/OpenSans-Light.ttf", 169 | "opensans/OpenSans-LightItalic.ttf", 170 | "opensans/OpenSans-Regular.ttf", 171 | "opensans/OpenSans-Semibold.ttf", 172 | "opensans/OpenSans-SemiboldItalic.ttf", 173 | 174 | "jetbrainsmono/JetBrainsMono-Bold.ttf" 175 | "jetbrainsmono/JetBrainsMono-Bold-Italic.ttf" 176 | "jetbrainsmono/JetBrainsMono-ExtraBold.ttf" 177 | "jetbrainsmono/JetBrainsMono-ExtraBold-Italic.ttf" 178 | "jetbrainsmono/JetBrainsMono-ExtraLight.ttf" 179 | "jetbrainsmono/JetBrainsMono-ExtraLight-Italic.ttf" 180 | "jetbrainsmono/JetBrainsMono-Italic.ttf" 181 | "jetbrainsmono/JetBrainsMono-Light.ttf" 182 | "jetbrainsmono/JetBrainsMono-Light-Italic.ttf" 183 | "jetbrainsmono/JetBrainsMono-Medium.ttf" 184 | "jetbrainsmono/JetBrainsMono-Medium-Italic.ttf" 185 | "jetbrainsmono/JetBrainsMono-Regular.ttf" 186 | "jetbrainsmono/JetBrainsMono-SemiLight.ttf" 187 | "jetbrainsmono/JetBrainsMono-SemiLight-Italic.ttf" 188 | ] 189 | 190 | for font in fonts: 191 | path = os.path.join(font_root, font) 192 | QtGui.QFontDatabase.addApplicationFont(path) 193 | -------------------------------------------------------------------------------- /docs/theme/extra.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Peinture Fraiche"; 3 | src: url("Peinture Fraiche.ttf") format("truetype"); 4 | } 5 | 6 | body, 7 | input { 8 | font-family: "Roboto", helvetica; 9 | } 10 | 11 | .md-grid { 12 | max-width: 57rem; 13 | } 14 | 15 | /* There's typically some space at the top of each article 16 | that we don't need, since we aren't showing h1 */ 17 | .md-content__inner:before { 18 | height: 0; 19 | } 20 | 21 | .md-content__inner { 22 | padding-top: 0; 23 | } 24 | 25 | /* Items in a list are typically too far apart */ 26 | .md-typeset ol li, .md-typeset ul li { 27 | margin-bottom: 5px; 28 | } 29 | 30 | /* Faded items in the table of contents, as the user scrolls */ 31 | .md-nav__link[data-md-state=blur] { 32 | color: rgba(0,0,0,.34); 33 | } 34 | 35 | .md-typeset, .md-sidebar { 36 | font-family: Segoe UI, 37 | Roboto, 38 | Oxygen, 39 | Ubuntu, 40 | Cantarell, 41 | Fira Sans, 42 | Droid Sans, 43 | Helvetica Neue, 44 | sans-serif; 45 | text-size-adjust: 100%; 46 | font-size: 16px; 47 | line-height: 1.4; 48 | color: #424242; 49 | font-weight: 400; 50 | -webkit-font-smoothing: antialiased; 51 | } 52 | 53 | .md-typeset code { 54 | box-shadow: none; 55 | border: 1px solid #eee; 56 | color: #222; 57 | padding: 0 3px; 58 | background-color: transparent; 59 | } 60 | 61 | .md-typeset ol, .md-typeset ul { 62 | line-height: 1.3em; 63 | } 64 | 65 | /* Reduce distance between help-boxes */ 66 | .md-typeset .admonition, .md-typeset details { 67 | margin: 6px 0; 68 | 69 | /* This is normally much too small */ 70 | font-size: inherit; 71 | } 72 | 73 | .md-typeset details.quote>summary { 74 | font-size: 0.7rem; 75 | font-weight: 400; 76 | } 77 | 78 | /* Remove padding from lists, it doesn't look good */ 79 | .md-typeset ol, .md-typeset ul { 80 | margin-left: 0; 81 | } 82 | 83 | .md-typeset .codehilite, .md-typeset .highlight { 84 | min-width: 50%; 85 | background: transparent; 86 | border: 1px solid #eee; 87 | margin-top: 0; 88 | } 89 | 90 | pre { 91 | color: #ccc !important; 92 | } 93 | 94 | .md-clipboard:before { 95 | color: rgb(200, 200, 200); 96 | } 97 | 98 | /* Hovering a link */ 99 | .md-nav__link:focus, .md-nav__link:hover { 100 | color: #86c8ef; 101 | } 102 | 103 | /* Default has some extra space here, 104 | which interfers with our use of background-color */ 105 | .md-nav__item:last-child { 106 | padding-bottom: 0px; 107 | } 108 | 109 | .codehilite:hover .md-clipboard:before,.md-typeset .highlight:hover .md-clipboard:before,pre:hover .md-clipboard:before { 110 | color: rgba(200, 200, 200, 0.54) !important 111 | } 112 | 113 | .codehilite code, .md-typeset .codehilite pre, .md-typeset .highlight code, .md-typeset .highlight pre { 114 | font-family: "Source Code Pro"; 115 | font-size: 0.9em; 116 | } 117 | 118 | input, select, textarea{ 119 | color: green; 120 | } 121 | 122 | .md-header[data-md-state] { 123 | box-shadow: none; 124 | border-bottom: 1px solid #f3f3f3; 125 | } 126 | 127 | .md-typeset a, .md-typeset a:before { 128 | color: #03a9f4; 129 | } 130 | 131 | .md-typeset h2 { 132 | font-family: "Peinture Fraiche"; 133 | font-size: 5em; 134 | color: #222; 135 | line-height: 0.8; 136 | } 137 | 138 | [data-md-color-primary=white] .md-search__input { 139 | background-color: transparent; 140 | border: 1px solid #f3f3f3; 141 | } 142 | 143 | h2, h3, h4, b, strong { 144 | color: #333; 145 | } 146 | 147 | .md-nav__item.md-nav__item--active { 148 | background-color: #e4f3ff; 149 | } 150 | 151 | .md-nav__link { 152 | margin-top: 0; 153 | padding-top: 4px; 154 | padding-bottom: 4px; 155 | } 156 | 157 | .md-typeset h1 { 158 | display: none; 159 | } 160 | 161 | .md-typeset h3, .md-typeset h4 { 162 | font-weight: bold; 163 | } 164 | 165 | .md-footer-copyright__highlight { 166 | display: none; 167 | } 168 | 169 | /* Hide left-sidebar header */ 170 | .md-nav__title--site { 171 | display: none; 172 | } 173 | 174 | .md-nav { 175 | font-size: .6rem; 176 | line-height: 1.3; 177 | color: #696969; 178 | } 179 | 180 | nav.md-nav.md-nav--primary { 181 | font-size: .65rem; 182 | } 183 | 184 | .md-footer { 185 | padding-top: 100px; 186 | } 187 | 188 | div.tabs { 189 | padding-left: 1px; 190 | } 191 | 192 | div.tabs button.active { 193 | color: #1f1f1f; 194 | position: relative; 195 | border-bottom: 6px solid steelblue; 196 | } 197 | 198 | div.tabs button { 199 | outline: none; 200 | cursor: pointer; 201 | position: relative; 202 | color: #6363635e; 203 | border: none; 204 | border-top-left-radius: 4px; 205 | border-top-right-radius: 4px; 206 | margin: 6px 14px 7px 0px; 207 | transition: 0.2s; 208 | font-size: 0.7rem; 209 | } 210 | 211 | .tab-content { 212 | background: white; 213 | display: none; 214 | padding: 0; 215 | border: none; 216 | } 217 | 218 | .md-typeset .tab-content .codehilitetable .linenos { 219 | display: none; 220 | } 221 | 222 | .md-typeset .tab-content .codehilitetable { 223 | margin: 0; 224 | } 225 | 226 | button.tab p { 227 | padding: 0; 228 | margin: 0; 229 | letter-spacing: 0.1rem; 230 | } 231 | 232 | button.tab div.tab-gap { 233 | position: absolute; 234 | width: 100%; 235 | height: 3px; 236 | bottom: -2px; 237 | margin: 0; 238 | padding: 0; 239 | background: #ffffff; 240 | z-index: 5; 241 | left: 0; 242 | display: none; 243 | } 244 | 245 | button.tab.active div.tab-gap { 246 | display: block; 247 | } 248 | 249 | div.tabs button.active p { 250 | text-shadow: none; 251 | } 252 | 253 | /* Only visible on a larger surface area (not mobile) */ 254 | .floating-image { 255 | display: none; 256 | } 257 | 258 | @media only screen and (min-width: 60em) { 259 | [data-md-toggle]:checked~.md-header .md-search__input+.md-search__icon, [data-md-toggle]:checked~.md-header .md-search__input::placeholder { 260 | color: rgba(0, 0, 0, .30); 261 | } 262 | 263 | .md-search__input+.md-search__icon, .md-search__input::placeholder { 264 | color: rgba(0, 0, 0, .30); 265 | } 266 | 267 | .floating-image { 268 | float: right; 269 | display: inherit; 270 | } 271 | 272 | } 273 | 274 | @media only screen and (min-width: 76.25em) { 275 | .md-sidebar--primary .md-sidebar__inner { 276 | border-right: 1px solid rgba(0, 0, 0, .07); 277 | } 278 | 279 | .md-sidebar--secondary .md-sidebar__inner { 280 | border-left: 1px solid rgba(0, 0, 0, .07); 281 | } 282 | 283 | } 284 | 285 | [data-md-color-primary=white] .md-nav__link--active, [data-md-color-primary=white] .md-nav__link:active { 286 | color: #03a9f4; 287 | } -------------------------------------------------------------------------------- /allzpark/vendor/transitions/extensions/locking.py: -------------------------------------------------------------------------------- 1 | """ 2 | transitions.extensions.factory 3 | ------------------------------ 4 | 5 | Adds locking to machine methods as well as model functions that trigger events. 6 | Additionally, the user can inject her/his own context manager into the machine if required. 7 | """ 8 | 9 | from collections import defaultdict 10 | from functools import partial 11 | from threading import Lock 12 | import inspect 13 | import warnings 14 | import logging 15 | 16 | from ..core import Machine, Event, listify 17 | 18 | _LOGGER = logging.getLogger(__name__) 19 | _LOGGER.addHandler(logging.NullHandler()) 20 | 21 | # this is a workaround for dill issues when partials and super is used in conjunction 22 | # without it, Python 3.0 - 3.3 will not support pickling 23 | # https://github.com/pytransitions/transitions/issues/236 24 | _super = super 25 | 26 | try: 27 | from contextlib import nested # Python 2 28 | from thread import get_ident 29 | # with nested statements now raise a DeprecationWarning. Should be replaced with ExitStack-like approaches. 30 | warnings.simplefilter('ignore', DeprecationWarning) 31 | 32 | except ImportError: 33 | from contextlib import ExitStack, contextmanager 34 | from threading import get_ident 35 | 36 | @contextmanager 37 | def nested(*contexts): 38 | """ Reimplementation of nested in Python 3. """ 39 | with ExitStack() as stack: 40 | for ctx in contexts: 41 | stack.enter_context(ctx) 42 | yield contexts 43 | 44 | 45 | class PicklableLock(object): 46 | """ A wrapper for threading.Lock which discards its state during pickling and 47 | is reinitialized unlocked when unpickled. 48 | """ 49 | 50 | def __init__(self): 51 | self.lock = Lock() 52 | 53 | def __getstate__(self): 54 | return '' 55 | 56 | def __setstate__(self, value): 57 | return self.__init__() 58 | 59 | def __enter__(self): 60 | self.lock.__enter__() 61 | 62 | def __exit__(self, exc_type, exc_val, exc_tb): 63 | self.lock.__exit__(exc_type, exc_val, exc_tb) 64 | 65 | 66 | class LockedEvent(Event): 67 | """ An event type which uses the parent's machine context map when triggered. """ 68 | 69 | def trigger(self, model, *args, **kwargs): 70 | """ Extends transitions.core.Event.trigger by using locks/machine contexts. """ 71 | # pylint: disable=protected-access 72 | # noinspection PyProtectedMember 73 | # LockedMachine._locked should not be called somewhere else. That's why it should not be exposed 74 | # to Machine users. 75 | if self.machine._locked != get_ident(): 76 | with nested(*self.machine.model_context_map[model]): 77 | return _super(LockedEvent, self).trigger(model, *args, **kwargs) 78 | else: 79 | return _super(LockedEvent, self).trigger(model, *args, **kwargs) 80 | 81 | 82 | class LockedMachine(Machine): 83 | """ Machine class which manages contexts. In it's default version the machine uses a `threading.Lock` 84 | context to lock access to its methods and event triggers bound to model objects. 85 | Attributes: 86 | machine_context (dict): A dict of context managers to be entered whenever a machine method is 87 | called or an event is triggered. Contexts are managed for each model individually. 88 | """ 89 | 90 | event_cls = LockedEvent 91 | 92 | def __init__(self, *args, **kwargs): 93 | self._locked = 0 94 | 95 | try: 96 | self.machine_context = listify(kwargs.pop('machine_context')) 97 | except KeyError: 98 | self.machine_context = [PicklableLock()] 99 | 100 | self.machine_context.append(self) 101 | self.model_context_map = defaultdict(list) 102 | 103 | _super(LockedMachine, self).__init__(*args, **kwargs) 104 | 105 | def add_model(self, model, initial=None, model_context=None): 106 | """ Extends `transitions.core.Machine.add_model` by `model_context` keyword. 107 | Args: 108 | model (list or object): A model (list) to be managed by the machine. 109 | initial (string or State): The initial state of the passed model[s]. 110 | model_context (list or object): If passed, assign the context (list) to the machines 111 | model specific context map. 112 | """ 113 | models = listify(model) 114 | model_context = listify(model_context) if model_context is not None else [] 115 | output = _super(LockedMachine, self).add_model(models, initial) 116 | 117 | for mod in models: 118 | mod = self if mod == 'self' else mod 119 | self.model_context_map[mod].extend(self.machine_context) 120 | self.model_context_map[mod].extend(model_context) 121 | 122 | return output 123 | 124 | def remove_model(self, model): 125 | """ Extends `transitions.core.Machine.remove_model` by removing model specific context maps 126 | from the machine when the model itself is removed. """ 127 | models = listify(model) 128 | 129 | for mod in models: 130 | del self.model_context_map[mod] 131 | 132 | return _super(LockedMachine, self).remove_model(models) 133 | 134 | def __getattribute__(self, item): 135 | get_attr = _super(LockedMachine, self).__getattribute__ 136 | tmp = get_attr(item) 137 | if not item.startswith('_') and inspect.ismethod(tmp): 138 | return partial(get_attr('_locked_method'), tmp) 139 | return tmp 140 | 141 | def __getattr__(self, item): 142 | try: 143 | return _super(LockedMachine, self).__getattribute__(item) 144 | except AttributeError: 145 | return _super(LockedMachine, self).__getattr__(item) 146 | 147 | # Determine if the returned method is a partial and make sure the returned partial has 148 | # not been created by Machine.__getattr__. 149 | # https://github.com/tyarkoni/transitions/issues/214 150 | def _add_model_to_state(self, state, model): 151 | _super(LockedMachine, self)._add_model_to_state(state, model) # pylint: disable=protected-access 152 | for prefix in ['enter', 'exit']: 153 | callback = "on_{0}_".format(prefix) + state.name 154 | func = getattr(model, callback, None) 155 | if isinstance(func, partial) and func.func != state.add_callback: 156 | state.add_callback(prefix, callback) 157 | 158 | def _locked_method(self, func, *args, **kwargs): 159 | if self._locked != get_ident(): 160 | with nested(*self.machine_context): 161 | return func(*args, **kwargs) 162 | else: 163 | return func(*args, **kwargs) 164 | 165 | def __enter__(self): 166 | self._locked = get_ident() 167 | 168 | def __exit__(self, *exc): 169 | self._locked = 0 170 | -------------------------------------------------------------------------------- /docs/pages/about.md: -------------------------------------------------------------------------------- 1 | This section outlines the rationale behind Allzpark, to help you determine whether or not it is of use to you. 2 | 3 |
4 | 5 | ### Background 6 | 7 | Allzpark (a.k.a. LaunchApp2) started as a 4-month commission for the Japanese [Studio Anima](http://www.studioanima.co.jp/). Time was divided into roughly these parts. 8 | 9 | 1. **Week 0-0** Tour of physical building, infrastructure and crew 10 | 1. **Week 1-2** Requirements gathering, an evaluation if current system 11 | 1. **Week 3-4** Evaluation of off-the-shelf options, e.g. Rez 12 | 1. **Week 5-6** Evaluation of studio, system and personnel resources 13 | 4. **Week 7-8** Integration and testing of fundamental infrastucture software, Ansible 14 | 5. **Week 9-10** Research and development of Rez to fit the criteria and initial [prototype](https://github.com/mottosso/rez-for-projects) 15 | 1. **Week 11-12** Conversion of existing package repository 16 | 1. **Week 13-14** Implementation of graphical user interface, LaunchApp2 17 | 1. **Week 15-16** Refinement of features, including localisation 18 | 1. **Week 17-18** Final integration and training of staff 19 | 20 |
21 | 22 | ### Journal 23 | 24 | Allzpark was initially an internal project, never intended to be open sourced. As a result, the first 2 months of development are locked away behind an internal journal for the company (due to disclosure of sensitive information). 25 | 26 | Luckily, it was around this time that Allzpark got approved for open source and when I was able to start sharing its development publicly, so that you are able to take part in the design decisions made, the why and how. This way, you're able to accurately determine whether a solution to a new problem takes the original requirements into consideration; something all too often lost in software projects. 27 | 28 | - [Journal](https://github.com/mottosso/allzpark/issues/1) 29 | 30 |
31 | 32 | ## Story time 33 | 34 | When Hannah - working at a digital production company like Framestore or ILM - arrives at work in the morning, she typically types something like this into her console. 35 | 36 | ```powershell 37 | go gravity 38 | maya 39 | ``` 40 | 41 | What this does is put Hannah in the "context" of the `gravity` project. The subsequent call to `maya` then launches a given application, in this case Autodesk Maya. But which version? And why does it matter? 42 | 43 |
44 | 45 | ### A closer look 46 | 47 | To better understand what's happening here, let's take a closer look at what these commands do. Following the command `go gravity`, a few things happen. 48 | 49 | 1. The argument `gravity` is correlated to a project (either on disk or database) 50 | 2. The project is associated with metadata, detailing what software and versions are in use 51 | - `maya-2015` 52 | - `arnold-4.12` 53 | - `mgear-2.4` 54 | - `fbake-4.1` 55 | - `fasset-1.14` 56 | - `...` 57 | 3. The associated software is loaded into command-line `environment` 58 | 59 | At this point, the subsequent command `maya` unambiguously refers to `maya-2015`, which is how Framestore - and virtually every visual effects, feature animation, commercial and games facility - is able to tie a specific version of each set of software to a given project. 60 | 61 | Why is this important? The answer lies in **interoperability**. 62 | 63 | You see, whatever comes out of Hannah's department must interoperate with subsequent departments. Like an assembly line, the pace of the line remains consistent till the end, and every tool depends on the output of whatever came before it. 64 | 65 | This holds true for individual applications, like Maya or Visual Studio, but also sub-components of applications - plug-ins. 66 | 67 | Take `arnold-4.12` as an example. This particular version needs to interoperate with `maya-2015`. 68 | 69 | ```powershell 70 | 2015 2016 2017 2018 2019 71 | maya |--------------------------| 72 | arnold-1 |-------| 73 | arnold-2 |-----------| 74 | arnold-3 |-----------| 75 | arnold-4 |----------| 76 | ``` 77 | 78 | In order to leverage `maya-2015` for a given project, your choice of `arnold` is limited to those that support it, or vice versa. 79 | 80 | ```powershell 81 | interoperable 82 | slice 83 | maya |-----------------|------|---| 84 | arnold-1 |-------| | | 85 | arnold-2 |-----------| | | 86 | arnold-3 |------|------| 87 | arnold-4 |-|------|---| 88 | | | 89 | ``` 90 | 91 | This issue is compounded by the number of libraries and plug-ins you use for a given project. Consider `openimageio`, `qt`, `ilmbase` and other off-the-shelf projects you may want to employ in a given project, and you can start to get some idea of how narrow 92 | 93 | It is then further compounded by in-house development projects, such as your [*pipeline*](http://getavalon.github.io). 94 | 95 | None of this would have been a problem, if you were able to say: 96 | 97 | 1. We will ever only work on a single project at a time 98 | 1. We know which versions to use 99 | 1. We don't develop any new software ourselves 100 | 101 | In which case you could simply install each of these applications and get to work. But more often than not, things change. And in order to facilitate this change, there needs to be a system in place to help manage the combinatorial complexity of applications, software, and projects. 102 | 103 |
104 | 105 | ### Rez Users 106 | 107 | Here are some of the studios using Rez today, along with some approximate numbers (sources linked). 108 | 109 | | Studio | Active | People | Disk | Packages | Versions | Frequency | Source 110 | |:---------------|:-------|:-------|:-------|:---------|:---------|:----------|:----------- 111 | | Anima | 2019- | 100 | 30 GB | 199 | 2133 | 5 / day | - 112 | | RodeoFX | 2019- | 200 | 223 GB | 400 | 6732 | - | [a][] 113 | | Animal Logic | 2018- | 999 | 2 TB | 1552 | 44939 | 20 / day | [a][] 114 | | Mackievision | 2019- | 500 | | | | | - 115 | | Imageworks | 2019- | 999 | | | | | - 116 | | Puppetworks | 2019- | 200 | | | | | - 117 | | ToonBox | 2017- | | | | | | [f][] 118 | | Pixomondo | 2019- | | | | | | [b][] 119 | | Freefolk | 2019- | | | | | | [b][] 120 | | MPC | 2019- | | | | | | [b][] 121 | | Squeeze Studio | 2019- | | | | | | [c][] 122 | | Mikros | 2019- | | | | | | [c][] 123 | | Brunch Studio | 2019- | | | | | | [d][] 124 | | WWFX | 2019- | | | | | | [e][] 125 | 126 | [a]: https://groups.google.com/forum/#!topic/rez-config/GMiof1NEQoo 127 | [b]: https://groups.google.com/forum/#!searchin/rez-config/advice$20or$20tips$20on$20getting$20latest$20%7Csort:date/rez-config/-fmvH5mv9wM/cCWqh9BlFQAJ 128 | [c]: https://groups.google.com/forum/#!searchin/rez-config/Proper$20way$20to$20resolve$20an$20environment$20for$20an$20embedded$20python$20environment%7Csort:date/rez-config/2IWclNTJEk0/4B_hGWuxBQAJ 129 | [d]: https://groups.google.com/forum/#!msg/rez-config/Z7NdidsJNUY/2zYgVKsoEAAJ 130 | [e]: https://groups.google.com/forum/#!topic/rez-config/j78X0Qv3arM 131 | [f]: https://github.com/nerdvegas/rez/commit/8ca303d 132 | -------------------------------------------------------------------------------- /allzpark/vendor/transitions/extensions/states.py: -------------------------------------------------------------------------------- 1 | """ 2 | transitions.extensions.states 3 | ----------------------------- 4 | 5 | This module contains mix ins which can be used to extend state functionality. 6 | """ 7 | 8 | from threading import Timer 9 | import logging 10 | import inspect 11 | 12 | from ..core import MachineError, listify, State 13 | 14 | _LOGGER = logging.getLogger(__name__) 15 | _LOGGER.addHandler(logging.NullHandler()) 16 | 17 | 18 | class Tags(State): 19 | """ Allows states to be tagged. 20 | Attributes: 21 | tags (list): A list of tag strings. `State.is_` may be used 22 | to check if is in the list. 23 | """ 24 | def __init__(self, *args, **kwargs): 25 | """ 26 | Args: 27 | **kwargs: If kwargs contains `tags`, assign them to the attribute. 28 | """ 29 | self.tags = kwargs.pop('tags', []) 30 | super(Tags, self).__init__(*args, **kwargs) 31 | 32 | def __getattr__(self, item): 33 | if item.startswith('is_'): 34 | return item[3:] in self.tags 35 | return super(Tags, self).__getattribute__(item) 36 | 37 | 38 | class Error(Tags): 39 | """ This mix in builds upon tag and should be used INSTEAD of Tags if final states that have 40 | not been tagged with 'accepted' should throw an `MachineError`. 41 | """ 42 | 43 | def __init__(self, *args, **kwargs): 44 | """ 45 | Args: 46 | **kwargs: If kwargs contains the keywork `accepted` add the 'accepted' tag to a tag list 47 | which will be forwarded to the Tags constructor. 48 | """ 49 | tags = kwargs.get('tags', []) 50 | accepted = kwargs.pop('accepted', False) 51 | if accepted: 52 | tags.append('accepted') 53 | kwargs['tags'] = tags 54 | super(Error, self).__init__(*args, **kwargs) 55 | 56 | def enter(self, event_data): 57 | """ Extends transitions.core.State.enter. Throws a `MachineError` if there is 58 | no leaving transition from this state and 'accepted' is not in self.tags. 59 | """ 60 | if not event_data.machine.get_triggers(self.name) and not self.is_accepted: 61 | raise MachineError("Error state '{0}' reached!".format(self.name)) 62 | super(Error, self).enter(event_data) 63 | 64 | 65 | class Timeout(State): 66 | """ Adds timeout functionality to a state. Timeouts are handled model-specific. 67 | Attributes: 68 | timeout (float): Seconds after which a timeout function should be called. 69 | on_timeout (list): Functions to call when a timeout is triggered. 70 | """ 71 | 72 | dynamic_methods = ['on_timeout'] 73 | 74 | def __init__(self, *args, **kwargs): 75 | """ 76 | Args: 77 | **kwargs: If kwargs contain 'timeout', assign the float value to self.timeout. If timeout 78 | is set, 'on_timeout' needs to be passed with kwargs as well or an AttributeError will 79 | be thrown. If timeout is not passed or equal 0. 80 | """ 81 | self.timeout = kwargs.pop('timeout', 0) 82 | self._on_timeout = None 83 | if self.timeout > 0: 84 | try: 85 | self.on_timeout = kwargs.pop('on_timeout') 86 | except KeyError: 87 | raise AttributeError("Timeout state requires 'on_timeout' when timeout is set.") 88 | else: 89 | self._on_timeout = kwargs.pop('on_timeout', []) 90 | self.runner = {} 91 | super(Timeout, self).__init__(*args, **kwargs) 92 | 93 | def enter(self, event_data): 94 | """ Extends `transitions.core.State.enter` by starting a timeout timer for the current model 95 | when the state is entered and self.timeout is larger than 0. 96 | """ 97 | if self.timeout > 0: 98 | timer = Timer(self.timeout, self._process_timeout, args=(event_data,)) 99 | timer.setDaemon(True) 100 | timer.start() 101 | self.runner[id(event_data.model)] = timer 102 | super(Timeout, self).enter(event_data) 103 | 104 | def exit(self, event_data): 105 | """ Extends `transitions.core.State.exit` by canceling a timer for the current model. """ 106 | timer = self.runner.get(id(event_data.model), None) 107 | if timer is not None and timer.is_alive(): 108 | timer.cancel() 109 | super(Timeout, self).exit(event_data) 110 | 111 | def _process_timeout(self, event_data): 112 | _LOGGER.debug("%sTimeout state %s. Processing callbacks...", event_data.machine.name, self.name) 113 | for callback in self.on_timeout: 114 | event_data.machine.callback(callback, event_data) 115 | _LOGGER.info("%sTimeout state %s processed.", event_data.machine.name, self.name) 116 | 117 | @property 118 | def on_timeout(self): 119 | """ List of strings and callables to be called when the state timeouts. """ 120 | return self._on_timeout 121 | 122 | @on_timeout.setter 123 | def on_timeout(self, value): 124 | """ Listifies passed values and assigns them to on_timeout.""" 125 | self._on_timeout = listify(value) 126 | 127 | 128 | class Volatile(State): 129 | """ Adds scopes/temporal variables to the otherwise persistent state objects. 130 | Attributes: 131 | volatile_cls (cls): Class of the temporal object to be initiated. 132 | volatile_hook (string): Model attribute name which will contain the volatile instance. 133 | """ 134 | 135 | def __init__(self, *args, **kwargs): 136 | """ 137 | Args: 138 | **kwargs: If kwargs contains `volatile`, always create an instance of the passed class 139 | whenever the state is entered. The instance is assigned to a model attribute which 140 | can be passed with the kwargs keyword `hook`. If hook is not passed, the instance will 141 | be assigned to the 'attribute' scope. If `volatile` is not passed, an empty object will 142 | be assigned to the model's hook. 143 | """ 144 | self.volatile_cls = kwargs.pop('volatile', VolatileObject) 145 | self.volatile_hook = kwargs.pop('hook', 'scope') 146 | super(Volatile, self).__init__(*args, **kwargs) 147 | self.initialized = True 148 | 149 | def enter(self, event_data): 150 | """ Extends `transitions.core.State.enter` by creating a volatile object and assign it to 151 | the current model's hook. """ 152 | setattr(event_data.model, self.volatile_hook, self.volatile_cls()) 153 | super(Volatile, self).enter(event_data) 154 | 155 | def exit(self, event_data): 156 | """ Extends `transitions.core.State.exit` by deleting the temporal object from the model. """ 157 | super(Volatile, self).exit(event_data) 158 | try: 159 | delattr(event_data.model, self.volatile_hook) 160 | except AttributeError: 161 | pass 162 | 163 | 164 | def add_state_features(*args): 165 | """ State feature decorator. Should be used in conjunction with a custom Machine class. """ 166 | def _class_decorator(cls): 167 | class CustomState(type('CustomState', args, {}), cls.state_cls): 168 | """ The decorated State. It is based on the State class used by the decorated Machine. """ 169 | pass 170 | 171 | method_list = sum([c.dynamic_methods for c in inspect.getmro(CustomState) if hasattr(c, 'dynamic_methods')], []) 172 | CustomState.dynamic_methods = set(method_list) 173 | cls.state_cls = CustomState 174 | return cls 175 | return _class_decorator 176 | 177 | 178 | class VolatileObject(object): 179 | """ Empty Python object which can be used to assign attributes to.""" 180 | pass 181 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | --- 2 | trigger: 3 | 4 | # Already default, but made explicit here 5 | branches: 6 | include: ["*"] 7 | 8 | # Ensure Azure triggers a build on a new tag 9 | # We use these for GitHub releases 10 | tags: 11 | include: ["*"] 12 | 13 | paths: 14 | # Do not trigger a build on changes at these paths 15 | exclude: 16 | - docs/* 17 | - .gitignore 18 | - LICENSE.txt 19 | - README.md 20 | 21 | 22 | jobs: 23 | 24 | # ----------------------------------------------------------------------- 25 | # 26 | # Test 27 | # 28 | # ----------------------------------------------------------------------- 29 | 30 | - job: Ubuntu 31 | pool: 32 | vmImage: "ubuntu-20.04" # Focal 33 | strategy: 34 | matrix: 35 | Py35-Rez: 36 | python.version: "3.5" 37 | rez.project: "rez" 38 | 39 | Py35-BleedingRez: 40 | python.version: "3.5" 41 | rez.project: "bleeding-rez" 42 | 43 | Py36-Rez: 44 | python.version: "3.6" 45 | rez.project: "rez" 46 | 47 | Py36-BleedingRez: 48 | python.version: "3.6" 49 | rez.project: "bleeding-rez" 50 | 51 | Py37-Rez: 52 | python.version: "3.7" 53 | rez.project: "rez" 54 | 55 | Py37-BleedingRez: 56 | python.version: "3.7" 57 | rez.project: "bleeding-rez" 58 | 59 | steps: 60 | - task: UsePythonVersion@0 61 | inputs: 62 | versionSpec: "$(python.version)" 63 | displayName: "Use Python $(python.version)" 64 | 65 | - script: | 66 | git clone https://github.com/nerdvegas/rez.git rez-src 67 | cd rez-src 68 | sudo pip install . 69 | condition: eq(variables['rez.project'], 'rez') 70 | displayName: "Install rez (pip for API)" 71 | 72 | - script: | 73 | sudo pip install bleeding-rez 74 | condition: eq(variables['rez.project'], 'bleeding-rez') 75 | displayName: "Install bleeding-rez" 76 | 77 | - script: | 78 | sudo apt-get install python-pyside 79 | sudo python -c "from PySide import QtCore;print(QtCore.__version__)" 80 | condition: startsWith(variables['python.version'], '2.') 81 | displayName: "Install PySide" 82 | 83 | - script: | 84 | sudo apt-get install python3-pyside2.qtcore \ 85 | python3-pyside2.qtgui \ 86 | python3-pyside2.qtwidgets \ 87 | python3-pyside2.qtsvg 88 | sudo pip install pyside2 89 | sudo python -c "from PySide2 import QtCore;print(QtCore.__version__)" 90 | condition: startsWith(variables['python.version'], '3.') 91 | displayName: "Install PySide2" 92 | 93 | - script: | 94 | sudo pip install nose 95 | displayName: "Install test tools" 96 | 97 | - script: | 98 | sudo pip install . --no-deps 99 | displayName: "Install allzpark" 100 | 101 | - script: | 102 | sudo apt-get install xvfb 103 | displayName: "Setup Xvfb" 104 | 105 | - script: | 106 | export DISPLAY=:99 107 | xvfb-run sudo nosetests 108 | displayName: "Run tests" 109 | 110 | 111 | - job: MacOS 112 | pool: 113 | vmImage: "macOS-10.15" 114 | strategy: 115 | matrix: 116 | Py37-Rez: 117 | python.version: "3.7" 118 | rez.project: "rez" 119 | 120 | Py37-BleedingRez: 121 | python.version: "3.7" 122 | rez.project: "bleeding-rez" 123 | 124 | steps: 125 | - task: UsePythonVersion@0 126 | inputs: 127 | versionSpec: "$(python.version)" 128 | displayName: "Use Python $(python.version)" 129 | 130 | - script: | 131 | git clone https://github.com/nerdvegas/rez.git rez-src 132 | cd rez-src 133 | pip install . 134 | condition: eq(variables['rez.project'], 'rez') 135 | displayName: "Install rez (pip for API)" 136 | 137 | - script: | 138 | pip install bleeding-rez 139 | condition: eq(variables['rez.project'], 'bleeding-rez') 140 | displayName: "Install bleeding-rez" 141 | 142 | - script: | 143 | brew tap cartr/qt4 144 | brew install qt@4 145 | pip install PySide 146 | condition: startsWith(variables['python.version'], '2.') 147 | displayName: "Install PySide" 148 | 149 | - script: | 150 | pip install PySide2 151 | condition: startsWith(variables['python.version'], '3.') 152 | displayName: "Install PySide2" 153 | 154 | - script: | 155 | pip install nose 156 | displayName: "Install test tools" 157 | 158 | - script: | 159 | pip install . --no-deps 160 | displayName: "Install allzpark" 161 | 162 | - script: | 163 | nosetests 164 | displayName: "Run tests" 165 | 166 | - job: Windows 167 | pool: 168 | vmImage: windows-latest 169 | strategy: 170 | matrix: 171 | Py37-Rez: 172 | python.version: "3.7" 173 | rez.project: "rez" 174 | 175 | Py37-BleedingRez: 176 | python.version: "3.7" 177 | rez.project: "bleeding-rez" 178 | 179 | steps: 180 | - task: UsePythonVersion@0 181 | inputs: 182 | versionSpec: "$(python.version)" 183 | displayName: "Use Python $(python.version)" 184 | 185 | - script: | 186 | git clone https://github.com/nerdvegas/rez.git rez-src 187 | cd rez-src 188 | pip install . 189 | condition: eq(variables['rez.project'], 'rez') 190 | displayName: "Install rez (pip for API)" 191 | 192 | - script: | 193 | pip install bleeding-rez 194 | condition: eq(variables['rez.project'], 'bleeding-rez') 195 | displayName: "Install bleeding-rez" 196 | 197 | - script: | 198 | pip install PySide 199 | condition: startsWith(variables['python.version'], '2.') 200 | displayName: "Install PySide" 201 | 202 | - script: | 203 | pip install PySide2 204 | condition: startsWith(variables['python.version'], '3.') 205 | displayName: "Install PySide2" 206 | 207 | - script: | 208 | pip install nose 209 | displayName: "Install test tools" 210 | 211 | - script: | 212 | pip install . --no-deps 213 | displayName: "Install allzpark" 214 | 215 | - script: | 216 | nosetests 217 | displayName: "Run tests" 218 | 219 | 220 | # ----------------------------------------------------------------------- 221 | # 222 | # Deploy to PyPI 223 | # 224 | # ----------------------------------------------------------------------- 225 | 226 | - job: Deploy 227 | condition: startsWith(variables['Build.SourceBranch'], 'refs/tags') 228 | pool: 229 | vmImage: "ubuntu-latest" 230 | strategy: 231 | matrix: 232 | Python37: 233 | python.version: "3.7" 234 | 235 | steps: 236 | - task: UsePythonVersion@0 237 | inputs: 238 | versionSpec: "$(python.version)" 239 | displayName: "Use Python $(python.version)" 240 | 241 | - script: | 242 | pip install wheel twine 243 | python setup.py sdist bdist_wheel 244 | echo [distutils] > ~/.pypirc 245 | echo index-servers=pypi >> ~/.pypirc 246 | echo [pypi] >> ~/.pypirc 247 | echo username=$_LOGIN >> ~/.pypirc 248 | echo password=$_PASSWORD >> ~/.pypirc 249 | twine upload dist/* 250 | displayName: "Deploy to PyPI" 251 | 252 | # Decrypt secret variables provided by Azure web console 253 | env: 254 | _LOGIN: $(PYPI_LOGIN) 255 | _PASSWORD: $(PYPI_PASSWORD) 256 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /docs/pages/avalon.md: -------------------------------------------------------------------------------- 1 | This page provides a transition guide from using [Avalon](https://getavalon.github.io)'s native Launcher to Allzpark. 2 | 3 |
4 | 5 | ### Walkthrough 6 | 7 | 8 | 9 |
10 | 11 | ### Quickstart 12 | 13 | This part assumes a successful [Quickstart](/quickstart). 14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 | ```powershell 23 | mkdir $env:USERPROFILE/projects 24 | subst P: $env:USERPROFILE/projects 25 | $env:REZ_PACKAGES_PATH="~/packages;$(allzparkdemo --packages)" 26 | $env:MY_PROFILES="bleed" 27 | ``` 28 | 29 |
30 | 31 |
32 | 33 | ```bash 34 | mkdir ~/projects 35 | $REZ_PACKAGES_PATH=~/packages:$(allzparkdemo --packages) 36 | $MY_PROFILES=bleed 37 | ``` 38 | 39 |
40 | 41 | ```bash 42 | # Convert PyPI packages to Rez 43 | git clone https://github.com/mottosso/rez-pipz.git 44 | cd rez-pipz 45 | rez build --install 46 | cd .. 47 | 48 | # Install PyPI dependencies 49 | rez env pipz -- install avalon-core avalon-colorbleed 50 | rez env pipz -- install pyqt5==5.8 # Any PyQt5 would do, this one requires Python 3 51 | 52 | # Install Avalon demo project 53 | git clone https://github.com/mottosso/bleed.git 54 | cd bleed 55 | rez build --install 56 | python -m avalon.inventory --save 57 | 58 | # Launch! 59 | allzpark --demo 60 | ``` 61 | 62 | ??? question "`~/projects`" 63 | For the purposes of this demo, the Avalon projects are assumed to be in this directory. 64 | 65 | ??? question "pipz" 66 | These are the base requirements for running Avalon 67 | 68 | ??? question "REZ_PACKAGES_PATH" 69 | For the purposes of this demo, we'll expose the Allzpark demo packages, primarily `maya`, `blender` and `rezutil` for use when building `bleed`. We could also have made a `maya`, `blender` and `rezutil` package globally, and skipped this step. 70 | 71 | ??? question "avalon.inventory" 72 | We'll also need this project uploaded to Avalon's MongoDB database. I'd like to skip this step, to instead automatically create projects post-launch, based on variables set in the Allzpark profile. 73 | 74 |
75 | 76 | ### Bleed Profile 77 | 78 | Let's have a look at how `bleed` is laid out. 79 | 80 | - [bleed/package.py](https://github.com/mottosso/bleed/blob/master/package.py) 81 | 82 | ```py 83 | name = "bleed" 84 | version = "1.0.15" 85 | build_command = "python -m rezutil build {root}" 86 | private_build_requires = ["rezutil-1"] 87 | ``` 88 | 89 | Nothing special here; we're building on the `rezutil` package from the Allzpark demo library to simplify the installation somewhat, it handles copying of the contained `userSetup.py`. 90 | 91 | ```py 92 | _requires = [ 93 | "~blender==2.80.0", 94 | "~maya==2015.0.0|2016.0.2|2017.0.4|2018.0.6", 95 | 96 | "pymongo-3.4+", 97 | 98 | "avalon_core-5.2+", 99 | "avalon_colorbleed-1", 100 | ] 101 | ``` 102 | 103 | Again we're referencing `blender` and `maya` from the demo library, in this case a number of versions of Maya to cover all bases. Allzpark displays every version that matches this pattern, in this case 4 versions of Maya. 104 | 105 | `pymongo` is the only real dependency to Avalon, the others being vendored, due to being the only one that isn't a pure-Python library. Finally, Avalon core and the colorbleed config is added are requirements to this profile. 106 | 107 | ```py 108 | @late() 109 | def requires(): 110 | global this 111 | global request 112 | global in_context 113 | 114 | requires = this._requires 115 | 116 | # Add request-specific requirements 117 | if in_context(): 118 | if "maya" in request: 119 | requires += [ 120 | "mgear", 121 | ] 122 | 123 | return requires 124 | ``` 125 | 126 | You'll notices the previous `_requires = []` had an underscore in it, which makes it invisible to Rez. Instead, we use this function `requires()` with a `@late()` decorator which makes Rez compute the requirements of this package when called, as opposed to when built. 127 | 128 | If called during built, the previously specified requirements are included. However, when called the `in_context()` function evaluates to `True` which in turn queries the request we made, e.g. `rez env bleed maya`, for whether "maya" was included. If so, then it goes ahead and appends `mgear` as another requirement for this profile. 129 | 130 | This is how you can specifiy *conditional* requirements for a given profile, requirements that come into effect only when used in combination with a particular set of requirements, like `maya`. In this case, `mgear` is only relevant to Maya, and not Blender. 131 | 132 | ```py 133 | def commands(): 134 | import os 135 | import tempfile 136 | 137 | global env 138 | global this 139 | global request 140 | 141 | # Better suited for a global/studio package 142 | projects = r"p:\projects" if os.name == "nt" else "~/projects" 143 | 144 | env["AVALON_PROJECTS"] = projects 145 | env["AVALON_CONFIG"] = "colorbleed" 146 | env["AVALON_PROJECT"] = this.name 147 | env["AVALON_EARLY_ADOPTER"] = "yes" 148 | ``` 149 | 150 | Next we give configure Allzpark with the necessary environment variables. 151 | 152 | - `AVALON_PROJECTS` is typically a global value for your studio, and better suited for a package required by every profile, like a `global` or `studio` package. I've included it here to keep the example self-contained. 153 | - `AVALON_CONFIG` here we reference the `avalon_colorbleed` requirement 154 | - `AVALON_PROJECT` storing the project name into the environment, referencing the `this` variable, which is the equivalent of `self` from within a class; it references the members from outside of the `commands()` function, in this case the `name` of the package itself; "bleed" 155 | - `AVALON_EARLY_ADOPTER` finally enabling some of the later features of Avalon 156 | 157 | ```py 158 | if "maya" in request: 159 | env["PYTHONPATH"].append("{root}/maya") # userSetup.py 160 | ``` 161 | 162 | Another conditional event; the `bleed` package includes a folder of profile-specific Maya scripts that are added to `PYTHONPATH` only if `maya` is part of the request. 163 | 164 | ```py 165 | env["AVALON_TASK"] = "modeling" 166 | env["AVALON_ASSET"] = "hero" 167 | env["AVALON_SILO"] = "asset" 168 | env["AVALON_WORKDIR"] = tempfile.gettempdir() 169 | ``` 170 | 171 | Finally, the members that we need to get rid of from the application launching process of Avalon; these need to happen post-launch. 172 | 173 |
174 | 175 | ### Differences 176 | 177 | Overall, Allzpark and Launcher are very similar. 178 | 179 | - **All Knowing** Launcher has all the information related to a project, asset and task 180 | - We'll need to split this responsibility and let Allzpark handle anything related to application startup, but leave assets and tasks to Avalon 181 | - **Working Directory** Launcher is responsible for creating a working directory, *prior* to application launch 182 | - Because is knows all of these things, it's a good fit for creating the initial working directory wherein an application saves data, like Maya's `workspace.mel` file and associated hierarchy. Because Allzpark isn't concerned with such things, we'll need to let the host deal with this. 183 | - An upside of this is that artists would then be able to switch task/asset/shot without a restart 184 | 185 |
186 | 187 | ### Todo 188 | 189 | - [ ] **DB** Create Avalon MongoDB project document post-launch 190 | - [ ] **Working Directory** Create working directory post-launch 191 | - E.g. via "Set Context" 192 | - [x] Create new assets interactively, rather than from .toml 193 | - I.e. launch "Project Manager" from Allzpark 194 | 195 | The above example works, but embeds too much information into the Allzpark profile, notably these: 196 | 197 | ```py 198 | env["AVALON_TASK"] = "modeling" 199 | env["AVALON_ASSET"] = "hero" 200 | env["AVALON_SILO"] = "asset" 201 | env["AVALON_WORKDIR"] = tempfile.gettempdir() 202 | ``` 203 | -------------------------------------------------------------------------------- /allzpark/vendor/QtImageViewer.py: -------------------------------------------------------------------------------- 1 | """ QtImageViewer.py: PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning. 2 | 3 | """ 4 | 5 | from .Qt.QtCore import Qt, QRectF, Signal 6 | from .Qt.QtGui import QImage, QPixmap, QPainterPath 7 | from .Qt.QtWidgets import QGraphicsView, QGraphicsScene 8 | 9 | from .Qt import QtCore, QtGui 10 | 11 | 12 | __author__ = "Marcel Goldschen-Ohm " 13 | __version__ = '0.9.0' 14 | 15 | 16 | class QtImageViewer(QGraphicsView): 17 | """ PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning. 18 | 19 | Displays a QImage or QPixmap (QImage is internally converted to a QPixmap). 20 | To display any other image format, you must first convert it to a QImage or QPixmap. 21 | 22 | Some useful image format conversion utilities: 23 | qimage2ndarray: NumPy ndarray <==> QImage (https://github.com/hmeine/qimage2ndarray) 24 | ImageQt: PIL Image <==> QImage (https://github.com/python-pillow/Pillow/blob/master/PIL/ImageQt.py) 25 | 26 | Mouse interaction: 27 | Left mouse button drag: Pan image. 28 | Right mouse button drag: Zoom box. 29 | Right mouse button doubleclick: Zoom to show entire image. 30 | """ 31 | 32 | # Mouse button signals emit image scene (x, y) coordinates. 33 | # !!! For image (row, column) matrix indexing, row = y and column = x. 34 | leftMouseButtonPressed = Signal(float, float) 35 | rightMouseButtonPressed = Signal(float, float) 36 | leftMouseButtonReleased = Signal(float, float) 37 | rightMouseButtonReleased = Signal(float, float) 38 | leftMouseButtonDoubleClicked = Signal(float, float) 39 | rightMouseButtonDoubleClicked = Signal(float, float) 40 | 41 | def __init__(self): 42 | QGraphicsView.__init__(self) 43 | 44 | # Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView. 45 | self.scene = QGraphicsScene() 46 | self.setScene(self.scene) 47 | self.setCursor(QtCore.Qt.PointingHandCursor) 48 | 49 | # Store a local handle to the scene's current image pixmap. 50 | self._pixmapHandle = None 51 | 52 | # Image aspect ratio mode. 53 | # !!! ONLY applies to full image. Aspect ratio is always ignored when zooming. 54 | # Qt.IgnoreAspectRatio: Scale image to fit viewport. 55 | # Qt.KeepAspectRatio: Scale image to fit inside viewport, preserving aspect ratio. 56 | # Qt.KeepAspectRatioByExpanding: Scale image to fill the viewport, preserving aspect ratio. 57 | self.aspectRatioMode = Qt.KeepAspectRatio 58 | 59 | # Scroll bar behaviour. 60 | # Qt.ScrollBarAlwaysOff: Never shows a scroll bar. 61 | # Qt.ScrollBarAlwaysOn: Always shows a scroll bar. 62 | # Qt.ScrollBarAsNeeded: Shows a scroll bar only when zoomed. 63 | self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 64 | self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 65 | 66 | # Stack of QRectF zoom boxes in scene coordinates. 67 | self.zoomStack = [] 68 | 69 | # Flags for enabling/disabling mouse interaction. 70 | self.canZoom = True 71 | self.canPan = True 72 | 73 | def hasImage(self): 74 | """ Returns whether or not the scene contains an image pixmap. 75 | """ 76 | return self._pixmapHandle is not None 77 | 78 | def clearImage(self): 79 | """ Removes the current image pixmap from the scene if it exists. 80 | """ 81 | if self.hasImage(): 82 | self.scene.removeItem(self._pixmapHandle) 83 | self._pixmapHandle = None 84 | 85 | def pixmap(self): 86 | """ Returns the scene's current image pixmap as a QPixmap, or else None if no image exists. 87 | :rtype: QPixmap | None 88 | """ 89 | if self.hasImage(): 90 | return self._pixmapHandle.pixmap() 91 | return None 92 | 93 | def image(self): 94 | """ Returns the scene's current image pixmap as a QImage, or else None if no image exists. 95 | :rtype: QImage | None 96 | """ 97 | if self.hasImage(): 98 | return self._pixmapHandle.pixmap().toImage() 99 | return None 100 | 101 | def setImage(self, image): 102 | """ Set the scene's current image pixmap to the input QImage or QPixmap. 103 | Raises a RuntimeError if the input image has type other than QImage or QPixmap. 104 | :type image: QImage | QPixmap 105 | """ 106 | if type(image) is QPixmap: 107 | pixmap = image 108 | elif type(image) is QImage: 109 | pixmap = QPixmap.fromImage(image) 110 | else: 111 | raise RuntimeError("ImageViewer.setImage: Argument must be a QImage or QPixmap.") 112 | if self.hasImage(): 113 | self._pixmapHandle.setPixmap(pixmap) 114 | else: 115 | self._pixmapHandle = self.scene.addPixmap(pixmap) 116 | 117 | self._pixmapHandle.setTransformationMode(QtCore.Qt.SmoothTransformation) 118 | self.setSceneRect(QRectF(pixmap.rect())) # Set scene size to image size. 119 | self.setRenderHints(QtGui.QPainter.Antialiasing | 120 | QtGui.QPainter.SmoothPixmapTransform) 121 | 122 | self.updateViewer() 123 | 124 | def updateViewer(self): 125 | """ Show current zoom (if showing entire image, apply current aspect ratio mode). 126 | """ 127 | if not self.hasImage(): 128 | return 129 | if len(self.zoomStack) and self.sceneRect().contains(self.zoomStack[-1]): 130 | self.fitInView(self.zoomStack[-1], Qt.KeepAspectRatio) # Show zoomed rect (ignore aspect ratio). 131 | else: 132 | self.zoomStack = [] # Clear the zoom stack (in case we got here because of an invalid zoom). 133 | self.fitInView(self.sceneRect(), self.aspectRatioMode) # Show entire image (use current aspect ratio mode). 134 | 135 | def resizeEvent(self, event): 136 | """ Maintain current zoom on resize. 137 | """ 138 | self.updateViewer() 139 | 140 | def mousePressEvent(self, event): 141 | """ Start mouse pan or zoom mode. 142 | """ 143 | scenePos = self.mapToScene(event.pos()) 144 | if event.button() == Qt.LeftButton: 145 | if self.canPan: 146 | self.setDragMode(QGraphicsView.ScrollHandDrag) 147 | self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y()) 148 | elif event.button() == Qt.RightButton: 149 | if self.canZoom: 150 | self.setDragMode(QGraphicsView.RubberBandDrag) 151 | self.rightMouseButtonPressed.emit(scenePos.x(), scenePos.y()) 152 | QGraphicsView.mousePressEvent(self, event) 153 | 154 | def mouseReleaseEvent(self, event): 155 | """ Stop mouse pan or zoom mode (apply zoom if valid). 156 | """ 157 | QGraphicsView.mouseReleaseEvent(self, event) 158 | scenePos = self.mapToScene(event.pos()) 159 | if event.button() == Qt.LeftButton: 160 | self.setDragMode(QGraphicsView.NoDrag) 161 | self.leftMouseButtonReleased.emit(scenePos.x(), scenePos.y()) 162 | elif event.button() == Qt.RightButton: 163 | if self.canZoom: 164 | viewBBox = self.zoomStack[-1] if len(self.zoomStack) else self.sceneRect() 165 | selectionBBox = self.scene.selectionArea().boundingRect().intersected(viewBBox) 166 | self.scene.setSelectionArea(QPainterPath()) # Clear current selection area. 167 | if selectionBBox.isValid() and (selectionBBox != viewBBox): 168 | self.zoomStack.append(selectionBBox) 169 | self.updateViewer() 170 | self.setDragMode(QGraphicsView.NoDrag) 171 | self.rightMouseButtonReleased.emit(scenePos.x(), scenePos.y()) 172 | 173 | def mouseDoubleClickEvent(self, event): 174 | """ Show entire image. 175 | """ 176 | scenePos = self.mapToScene(event.pos()) 177 | if event.button() == Qt.LeftButton: 178 | self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) 179 | elif event.button() == Qt.RightButton: 180 | if self.canZoom: 181 | self.zoomStack = [] # Clear zoom stack. 182 | self.updateViewer() 183 | self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) 184 | QGraphicsView.mouseDoubleClickEvent(self, event) 185 | -------------------------------------------------------------------------------- /docs/pages/rez.md: -------------------------------------------------------------------------------- 1 | This page is dedicated to learning Rez by example, utilising more of Rez's functionality as we go. 2 | 3 |
4 | 5 | ## Basics 6 | 7 | Let's start with the basics. 8 | 9 |
10 | 11 | ### Shortest Possible Example 12 | 13 | Create and use a new package from scratch in under 40 seconds. 14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 | ```powershell 23 | mkdir mypackage # Name of your Git project 24 | cd mypackage # Rez definition 25 | @" 26 | name = "mypackage" # Rez package name 27 | version = "1.0" # Rez package version 28 | build_command = False # Called when building package 29 | "@ | Add-Content package.py 30 | rez build --install # Build package 31 | rez env mypackage # Use package 32 | > # A new environment with your package 33 | ``` 34 | 35 |
36 | 37 |
38 | 39 | ```bash 40 | mkdir mypackage # Name of your Git project 41 | cd mypackage # Rez definition 42 | echo name = "mypackage" >> package.py # Rez package name 43 | echo version = "1.0" >> package.py # Rez package version 44 | echo build_command = False >> package.py # Called when building package 45 | rez build --install # Build package 46 | rez env mypackage # Use package 47 | > # A new environment with your package 48 | ``` 49 | 50 |
51 | 52 | - The `>` symbol means you are in a Rez "context". 53 | - Type `exit` to exit the context. 54 | 55 |
56 | 57 | ### Environment Variables 58 | 59 | Most packages will modify their environment in some way. 60 | 61 | **package.py** 62 | 63 | ```python 64 | name = "mypackage" 65 | version = "1.1" 66 | build_command = False 67 | 68 | def commands(): 69 | global env # Global variable available to `commands()` 70 | env["MYVARIABLE"] = "Yes" 71 | ``` 72 | 73 | This package will assign `"Yes"` to MYVARIABLE. 74 | 75 | - `env` A global Python variable representing the environment 76 | - `env["MYVARIABLE"]` - An environment variable 77 | - `env.MYVARIABLE` - This is also OK 78 | 79 |
80 | 81 | 82 |
83 | 84 |
85 | 86 | ```powershell 87 | rez build --install 88 | rez env mypackage 89 | > $env:MYVARIABLE 90 | # Yes 91 | ``` 92 | 93 |
94 | 95 |
96 | 97 | ```bash 98 | rez build --install 99 | rez env mypackage 100 | > echo $MYVARIABLE 101 | # Yes 102 | ``` 103 | 104 |
105 | 106 |
107 | 108 | ### Environment Paths 109 | 110 | A package can also modify paths, like `PATH` and `PYTHONPATH`, without removing what was there before. 111 | 112 | **package.py** 113 | 114 | ```python 115 | name = "mypackage" 116 | version = "1.2" 117 | build_command = False 118 | 119 | def commands(): 120 | global env 121 | env["PYTHONPATH"].prepend("{root}") 122 | env["PYTHONPATH"].prepend("{root}/python") 123 | ``` 124 | 125 | This package will assign `"{root}"` to `PYTHONPATH`. 126 | 127 | - `{root}` expands to the absolute path to the installed package 128 | - `env["PYTHONPATH"].prepend()` - Prepend a value to this variable 129 | - `env["PYTHONPATH"].append()` - Append a value to this variable 130 | 131 |
132 | 133 | 134 |
135 | 136 |
137 | 138 | ```powershell 139 | rez build --install 140 | rez env mypackage 141 | > $env:PYTHONPATH 142 | # \\server\packages\mypackage\1.2;\\server\packages\int\mypackage\1.2\python 143 | ``` 144 | 145 |
146 | 147 |
148 | 149 | ```bash 150 | rez build --install 151 | rez env mypackage 152 | > echo $PYTHONPATH 153 | # \server\packages\mypackage\1.2:\server\packages\int\mypackage\1.2\python 154 | ``` 155 | 156 |
157 | 158 |
159 | 160 | ### Requirements 161 | 162 | Most packages will depend on another package. 163 | 164 |
165 | 166 | 167 |
168 | 169 |
170 | 171 | ```powershell 172 | cd mypackage 173 | cd .. 174 | mkdir mypackage2 175 | $null >> mypackage2/package.py 176 | ``` 177 | 178 |
179 | 180 |
181 | 182 | ```bash 183 | cd mypackage 184 | cd .. 185 | mkdir mypackage2 186 | touch mypackage2/package.py 187 | ``` 188 | 189 |
190 | 191 | **mypackage2/package.py** 192 | 193 | ```python 194 | name = "mypackage2" 195 | version = "1.0" 196 | build_command = False 197 | requires = ["python-3", "mypackage-1.2"] 198 | ``` 199 | 200 | This package now requires `python-3` and `mypackage-1.2`. 201 | 202 | ```bash 203 | rez build --install 204 | rez env mypackage2 205 | # resolved by manima@toy, on Thu Jun 27 11:12:18 2019, using Rez v2.32.1 206 | # 207 | # requested packages: 208 | # mypackage2 209 | # ~platform==windows (implicit) 210 | # ~arch==AMD64 (implicit) 211 | # ~os==windows-10.0.18362.SP0 (implicit) 212 | # 213 | # resolved packages: 214 | # arch-AMD64 C:\Users\manima\packages\arch\AMD64 (local) 215 | # mypackage-1.3 C:\Users\manima\packages\mypackage\1.3 (local) 216 | # mypackage2-1.0 C:\Users\manima\packages\mypackage2\1.0 (local) 217 | # platform-windows C:\Users\manima\packages\platform\windows (local) 218 | # python-3.7.3 C:\Users\manima\packages\python\3.7.3\platform-windows\arch-AMD64 (local) 219 | > 220 | ``` 221 | 222 |
223 | 224 | ### Payload 225 | 226 | Most packages will have additional files, such as Python modules. This is where `build_command` comes in. 227 | 228 |
229 | 230 | 231 |
232 | 233 |
234 | 235 | ```powershell 236 | cd mypackage 237 | $null >> install.py # Additional script for build 238 | mkdir python # Payload directory 239 | cd python # 240 | "print('Hello World!')" | Add-Content mymodule.py # Python payload shipped alongside package 241 | ``` 242 | 243 |
244 | 245 |
246 | 247 | ```bash 248 | cd mypackage 249 | touch install.py # Additional script for build 250 | mkdir python # Payload directory 251 | cd python # 252 | echo print("Hello World!") >> mymodule.py # Python payload shipped alongside package 253 | ``` 254 | 255 |
256 | 257 | **package.py** 258 | 259 | ```python 260 | name = "mypackage" 261 | version = "1.3" 262 | build_command = "python {root}/install.py" # Run this command on `rez build` 263 | requires = ["python-3"] 264 | 265 | def commands(): 266 | global env 267 | env["PYTHONPATH"].prepend("{root}/python") # Add payload to environment 268 | ``` 269 | 270 | **install.py** 271 | 272 | ```python 273 | # This script is called on `rez build` 274 | import os 275 | import shutil 276 | 277 | print("Running install.py...") 278 | root = os.path.dirname(__file__) 279 | build_dir = os.environ["REZ_BUILD_PATH"] 280 | install_dir = os.environ["REZ_BUILD_INSTALL_PATH"] 281 | 282 | print("Copying payload to %s.." % build_dir) 283 | shutil.copytree( 284 | os.path.join(root, "python"), 285 | os.path.join(build_dir, "python"), 286 | ignore=shutil.ignore_patterns("*.pyc", "__pycache__") 287 | ) 288 | 289 | if int(os.getenv("REZ_BUILD_INSTALL")): 290 | # This part is called with `rez build --install` 291 | print("Installing payload to %s..." % install_dir) 292 | shutil.copytree( 293 | os.path.join(build_dir, "python"), 294 | os.path.join(install_dir, "python"), 295 | ) 296 | ``` 297 | 298 | Now let's build it. 299 | 300 | ```bash 301 | rez build --install 302 | rez env mypackage 303 | > python -m mymodule 304 | # Hello World! 305 | ``` 306 | -------------------------------------------------------------------------------- /docs/pages/index.md: -------------------------------------------------------------------------------- 1 | template: landing.html 2 | 3 |
4 | 5 |
6 |
7 |

Allzpark

8 |

9 | 10 | 11 | 12 | 13 | Windows 14 | Linux 15 | MacOS 16 |
17 | 18 | 19 | 20 |

21 |
22 |

23 | Powerful application launcher with reproducible software environments, for visual effects, feature animation and triple-A game productions. 24 |

25 |
26 |
27 |
28 | Learn more 29 | Download 30 | Blog 31 |
32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 | 50 | 51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |

Package Based

59 |

60 | Works on your machine? 61 |
62 |
63 | Allzpark is a package-based launcher, which means that everything related to a project is encapsulated into individual, version controlled and dependency managed "packages". Each coming together to form an environment identical across your development machine and anywhere your software is used. 64 |
65 |

66 | 67 | ```python 68 | # A package definition 69 | name = "blender" 70 | version = "2.80" 71 | 72 | def commands(): 73 | global env 74 | env["PATH"].append("{root}/bin") 75 | env["PYTHONPATH"].prepend("{root}/python") 76 | ``` 77 | 78 |

79 | Establish complex relationships between software, applications and projects with bleeding-rez, the underlying framework powering Allzpark. 80 |

81 |
82 |
83 |
84 | 85 |
86 |
87 | 88 | 89 | 94 | 95 |
96 |
97 |
98 |

Dual Representation

99 | 100 |

101 | Allzpark is but a shell. 102 | 103 | Anything done via the GUI is available via the command-line, using standard Rez commands. 104 |

105 | 106 |
107 | 108 | 109 |
110 | 111 |
112 | 113 | ```powershell 114 | PS> rez env alita maya -q 115 | > PS> echo "Hello Rez!" 116 | > # Hello Rez! 117 | ``` 118 | 119 |
120 | 121 |
122 | 123 | ```bash 124 | $ rez env alita maya -q 125 | > $ echo "Hello Rez!" 126 | # Hello Rez! 127 | ``` 128 | 129 |
130 | 131 |
132 |
133 |
134 |
135 | 136 |
137 |
138 | 139 |
140 |
141 | 142 | 147 | 148 |
149 |
150 |
151 |

Localisation

152 |

153 | 154 | Slow network? Working remotely? 155 |

156 | Reap optimal performance across the slowest of networks and disks with localisation, by turning any package into a locally accessible resource. 157 | 158 |

159 | 160 | 161 |
162 | 163 |
164 | 165 | ```powershell 166 | PS> rez env localz -- localize python-3 alita maya-2019 bifrost 167 | ``` 168 | 169 |
170 | 171 |
172 | 173 | ```bash 174 | $ rez env localz -- localize python-3 alita maya-2019 bifrost 175 | ``` 176 | 177 |
178 | 179 |

180 |
181 |
182 |
183 |
184 | 185 |
186 |
187 | 188 |
189 |
190 | 191 | 196 | 197 |
198 |
199 |
200 |

Environment Management

201 |

202 | 203 | Preview the environment, prior to launching an application. Make changes interactively as you develop or debug complex dependency chains. 204 | 205 |

206 |
207 |
208 |
209 |
210 | 211 |
212 |
213 | 214 |
215 |
216 | 217 | 222 | 223 |
224 |
225 |
226 |

Customisation

227 |

228 | Full theming support with pre-made color palettes to choose from. Interactively edit the underlying CSS and store them as your own. 229 |
230 |
231 | Drag panels around, establish a super-layout with everything visible at once. 232 |

233 |
234 |
235 |
236 |
237 | 238 |
239 |
240 | 241 |
242 |
243 | 244 | 249 | 250 |
251 |
252 |
253 |

Profiles

254 |

255 | 256 | Manage multiple projects with "profiles". 257 | 258 |

259 |
260 |
261 |
262 |
263 | 264 |
265 |
266 | 267 |
268 |
269 | 270 | 271 |
272 |
273 |
274 |
275 |
276 |
277 | 278 |
279 |

Allzpark

280 |

281 | Allzpark is free and open source (LGPL) 282 |
283 | Let's get this show on the road 284 |

285 |
286 | Learn more 287 | Download 288 |
289 |
290 | 291 |
292 |
293 |
294 | --------------------------------------------------------------------------------