From f47eec47878f08fadf4581f9724acb7a484845cb Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 24 Nov 2020 00:04:13 +0100 Subject: [PATCH 001/219] update changelog --- CHANGELOG.md | 46 +++++++ HISTORY.md | 360 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b96fb4c3..70e23e0ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## [2.14.0](https://github.com/pypeclub/pype/tree/2.14.0) (2020-11-24) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.7...2.14.0) + +**Enhancements:** + +- Shot asset build trigger status [\#736](https://github.com/pypeclub/pype/pull/736) +- Maya: add camera rig publishing option [\#721](https://github.com/pypeclub/pype/pull/721) +- Sort instances by label in pyblish gui [\#719](https://github.com/pypeclub/pype/pull/719) +- Synchronize ftrack hierarchical and shot attributes [\#716](https://github.com/pypeclub/pype/pull/716) +- 686 standalonepublisher editorial from image sequences [\#699](https://github.com/pypeclub/pype/pull/699) +- TV Paint: initial implementation of creators and local rendering [\#693](https://github.com/pypeclub/pype/pull/693) +- Render publish plugins abstraction [\#687](https://github.com/pypeclub/pype/pull/687) +- Ask user to select non-default camera from scene or create a new. [\#678](https://github.com/pypeclub/pype/pull/678) +- TVPaint: image loader with options [\#675](https://github.com/pypeclub/pype/pull/675) +- Maya: Camera name can be added to burnins. [\#674](https://github.com/pypeclub/pype/pull/674) +- After Effects: base integration with loaders [\#667](https://github.com/pypeclub/pype/pull/667) +- Harmony: Javascript refactoring and overall stability improvements [\#666](https://github.com/pypeclub/pype/pull/666) + +**Fixed bugs:** + +- TVPaint extract review fix [\#740](https://github.com/pypeclub/pype/pull/740) +- After Effects: Review were not being sent to ftrack [\#738](https://github.com/pypeclub/pype/pull/738) +- Asset fetch second fix [\#726](https://github.com/pypeclub/pype/pull/726) +- Maya: vray proxy was not loading [\#722](https://github.com/pypeclub/pype/pull/722) +- Maya: Vray expected file fixes [\#682](https://github.com/pypeclub/pype/pull/682) + +**Deprecated:** + +- Removed artist view from pyblish gui [\#717](https://github.com/pypeclub/pype/pull/717) +- Maya: disable legacy override check for cameras [\#715](https://github.com/pypeclub/pype/pull/715) + + +## [2.13.7](https://github.com/pypeclub/pype/tree/2.13.7) (2020-11-19) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.6...2.13.7) + +**Merged pull requests:** + +- fix\(SP\): getting fps from context instead of nonexistent entity [\#729](https://github.com/pypeclub/pype/pull/729) + +# Changelog + ## [2.13.6](https://github.com/pypeclub/pype/tree/2.13.6) (2020-11-15) [Full Changelog](https://github.com/pypeclub/pype/compare/2.13.5...2.13.6) @@ -789,4 +832,7 @@ A large cleanup release. Most of the change are under the hood. - _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* + + \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/HISTORY.md b/HISTORY.md index d60bd7b0c7..b8b96fb4c3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,360 @@ +# Changelog + +## [2.13.6](https://github.com/pypeclub/pype/tree/2.13.6) (2020-11-15) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.5...2.13.6) + +**Fixed bugs:** + +- Maya workfile version wasn't syncing with renders properly [\#711](https://github.com/pypeclub/pype/pull/711) +- Maya: Fix for publishing multiple cameras with review from the same scene [\#710](https://github.com/pypeclub/pype/pull/710) + +## [2.13.5](https://github.com/pypeclub/pype/tree/2.13.5) (2020-11-12) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.4...2.13.5) + +**Enhancements:** + +- 3.0 lib refactor [\#664](https://github.com/pypeclub/pype/issues/664) + +**Fixed bugs:** + +- Wrong thumbnail file was picked when publishing sequence in standalone publisher [\#703](https://github.com/pypeclub/pype/pull/703) +- Fix: Burnin data pass and FFmpeg tool check [\#701](https://github.com/pypeclub/pype/pull/701) + +## [2.13.4](https://github.com/pypeclub/pype/tree/2.13.4) (2020-11-09) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.3...2.13.4) + +**Enhancements:** + +- AfterEffects integration with Websocket [\#663](https://github.com/pypeclub/pype/issues/663) + +**Fixed bugs:** + +- Photoshop uhiding hidden layers [\#688](https://github.com/pypeclub/pype/issues/688) +- \#688 - Fix publishing hidden layers [\#692](https://github.com/pypeclub/pype/pull/692) + +**Closed issues:** + +- Nuke Favorite directories "shot dir" "project dir" - not working [\#684](https://github.com/pypeclub/pype/issues/684) + +**Merged pull requests:** + +- Nuke Favorite directories "shot dir" "project dir" - not working \#684 [\#685](https://github.com/pypeclub/pype/pull/685) + +## [2.13.3](https://github.com/pypeclub/pype/tree/2.13.3) (2020-11-03) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.2...2.13.3) + +**Enhancements:** + +- TV paint base integration [\#612](https://github.com/pypeclub/pype/issues/612) + +**Fixed bugs:** + +- Fix ffmpeg executable path with spaces [\#680](https://github.com/pypeclub/pype/pull/680) +- Hotfix: Added default version number [\#679](https://github.com/pypeclub/pype/pull/679) + +## [2.13.2](https://github.com/pypeclub/pype/tree/2.13.2) (2020-10-28) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.1...2.13.2) + +**Fixed bugs:** + +- Nuke: wrong conditions when fixing legacy write nodes [\#665](https://github.com/pypeclub/pype/pull/665) + +## [2.13.1](https://github.com/pypeclub/pype/tree/2.13.1) (2020-10-23) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.13.0...2.13.1) + +**Enhancements:** + +- move maya look assigner to pype menu [\#292](https://github.com/pypeclub/pype/issues/292) + +**Fixed bugs:** + +- Layer name is not propagating to metadata in Photoshop [\#654](https://github.com/pypeclub/pype/issues/654) +- Loader in Photoshop fails with "can't set attribute" [\#650](https://github.com/pypeclub/pype/issues/650) +- Nuke Load mp4 wrong frame range [\#661](https://github.com/pypeclub/pype/issues/661) +- Hiero: Review video file adding one frame to the end [\#659](https://github.com/pypeclub/pype/issues/659) + +## [2.13.0](https://github.com/pypeclub/pype/tree/2.13.0) (2020-10-18) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.5...2.13.0) + +**Enhancements:** + +- Deadline Output Folder [\#636](https://github.com/pypeclub/pype/issues/636) +- Nuke Camera Loader [\#565](https://github.com/pypeclub/pype/issues/565) +- Deadline publish job shows publishing output folder [\#649](https://github.com/pypeclub/pype/pull/649) +- Get latest version in lib [\#642](https://github.com/pypeclub/pype/pull/642) +- Improved publishing of multiple representation from SP [\#638](https://github.com/pypeclub/pype/pull/638) +- Launch TvPaint shot work file from within Ftrack [\#631](https://github.com/pypeclub/pype/pull/631) +- Add mp4 support for RV action. [\#628](https://github.com/pypeclub/pype/pull/628) +- Maya: allow renders to have version synced with workfile [\#618](https://github.com/pypeclub/pype/pull/618) +- Renaming nukestudio host folder to hiero [\#617](https://github.com/pypeclub/pype/pull/617) +- Harmony: More efficient publishing [\#615](https://github.com/pypeclub/pype/pull/615) +- Ftrack server action improvement [\#608](https://github.com/pypeclub/pype/pull/608) +- Deadline user defaults to pype username if present [\#607](https://github.com/pypeclub/pype/pull/607) +- Standalone publisher now has icon [\#606](https://github.com/pypeclub/pype/pull/606) +- Nuke render write targeting knob improvement [\#603](https://github.com/pypeclub/pype/pull/603) +- Animated pyblish gui [\#602](https://github.com/pypeclub/pype/pull/602) +- Maya: Deadline - make use of asset dependencies optional [\#591](https://github.com/pypeclub/pype/pull/591) +- Nuke: Publishing, loading and updating alembic cameras [\#575](https://github.com/pypeclub/pype/pull/575) +- Maya: add look assigner to pype menu even if scriptsmenu is not available [\#573](https://github.com/pypeclub/pype/pull/573) +- Store task types in the database [\#572](https://github.com/pypeclub/pype/pull/572) +- Maya: Tiled EXRs to scanline EXRs render option [\#512](https://github.com/pypeclub/pype/pull/512) +- Fusion basic integration [\#452](https://github.com/pypeclub/pype/pull/452) + +**Fixed bugs:** + +- Burnin script did not propagate ffmpeg output [\#640](https://github.com/pypeclub/pype/issues/640) +- Pyblish-pype spacer in terminal wasn't transparent [\#646](https://github.com/pypeclub/pype/pull/646) +- Lib subprocess without logger [\#645](https://github.com/pypeclub/pype/pull/645) +- Nuke: prevent crash if we only have single frame in sequence [\#644](https://github.com/pypeclub/pype/pull/644) +- Burnin script logs better output [\#641](https://github.com/pypeclub/pype/pull/641) +- Missing audio on farm submission. [\#639](https://github.com/pypeclub/pype/pull/639) +- review from imagesequence error [\#633](https://github.com/pypeclub/pype/pull/633) +- Hiero: wrong order of fps clip instance data collecting [\#627](https://github.com/pypeclub/pype/pull/627) +- Add source for review instances. [\#625](https://github.com/pypeclub/pype/pull/625) +- Task processing in event sync [\#623](https://github.com/pypeclub/pype/pull/623) +- sync to avalon doesn t remove renamed task [\#619](https://github.com/pypeclub/pype/pull/619) +- Intent publish setting wasn't working with default value [\#562](https://github.com/pypeclub/pype/pull/562) +- Maya: Updating a look where the shader name changed, leaves the geo without a shader [\#514](https://github.com/pypeclub/pype/pull/514) + +**Merged pull requests:** + +- Avalon module without Qt [\#581](https://github.com/pypeclub/pype/pull/581) +- Ftrack module without Qt [\#577](https://github.com/pypeclub/pype/pull/577) + +## [2.12.5](https://github.com/pypeclub/pype/tree/2.12.5) (2020-10-14) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.4...2.12.5) + +**Enhancements:** + +- Launch TvPaint shot work file from within Ftrack [\#629](https://github.com/pypeclub/pype/issues/629) + +**Merged pull requests:** + +- Harmony: Disable application launch logic [\#637](https://github.com/pypeclub/pype/pull/637) + +## [2.12.4](https://github.com/pypeclub/pype/tree/2.12.4) (2020-10-08) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.3...2.12.4) + +**Enhancements:** + +- convert nukestudio to hiero host [\#616](https://github.com/pypeclub/pype/issues/616) +- Fusion basic integration [\#451](https://github.com/pypeclub/pype/issues/451) + +**Fixed bugs:** + +- Sync to avalon doesn't remove renamed task [\#605](https://github.com/pypeclub/pype/issues/605) +- NukeStudio: FPS collecting into clip instances [\#624](https://github.com/pypeclub/pype/pull/624) + +**Merged pull requests:** + +- NukeStudio: small fixes [\#622](https://github.com/pypeclub/pype/pull/622) +- NukeStudio: broken order of plugins [\#620](https://github.com/pypeclub/pype/pull/620) + +## [2.12.3](https://github.com/pypeclub/pype/tree/2.12.3) (2020-10-06) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.2...2.12.3) + +**Enhancements:** + +- Nuke Publish Camera [\#567](https://github.com/pypeclub/pype/issues/567) +- Harmony: open xstage file no matter of its name [\#526](https://github.com/pypeclub/pype/issues/526) +- Stop integration of unwanted data [\#387](https://github.com/pypeclub/pype/issues/387) +- Move avalon-launcher functionality to pype [\#229](https://github.com/pypeclub/pype/issues/229) +- avalon workfiles api [\#214](https://github.com/pypeclub/pype/issues/214) +- Store task types [\#180](https://github.com/pypeclub/pype/issues/180) +- Avalon Mongo Connection split [\#136](https://github.com/pypeclub/pype/issues/136) +- nk camera workflow [\#71](https://github.com/pypeclub/pype/issues/71) +- Hiero integration added [\#590](https://github.com/pypeclub/pype/pull/590) +- Anatomy instance data collection is substantially faster for many instances [\#560](https://github.com/pypeclub/pype/pull/560) + +**Fixed bugs:** + +- test issue [\#596](https://github.com/pypeclub/pype/issues/596) +- Harmony: empty scene contamination [\#583](https://github.com/pypeclub/pype/issues/583) +- Edit publishing in SP doesn't respect shot selection for publishing [\#542](https://github.com/pypeclub/pype/issues/542) +- Pathlib breaks compatibility with python2 hosts [\#281](https://github.com/pypeclub/pype/issues/281) +- Updating a look where the shader name changed leaves the geo without a shader [\#237](https://github.com/pypeclub/pype/issues/237) +- Better error handling [\#84](https://github.com/pypeclub/pype/issues/84) +- Harmony: function signature [\#609](https://github.com/pypeclub/pype/pull/609) +- Nuke: gizmo publishing error [\#594](https://github.com/pypeclub/pype/pull/594) +- Harmony: fix clashing namespace of called js functions [\#584](https://github.com/pypeclub/pype/pull/584) +- Maya: fix maya scene type preset exception [\#569](https://github.com/pypeclub/pype/pull/569) + +**Closed issues:** + +- Nuke Gizmo publishing [\#597](https://github.com/pypeclub/pype/issues/597) +- nuke gizmo publishing error [\#592](https://github.com/pypeclub/pype/issues/592) +- Publish EDL [\#579](https://github.com/pypeclub/pype/issues/579) +- Publish render from SP [\#576](https://github.com/pypeclub/pype/issues/576) +- rename ftrack custom attribute group to `pype` [\#184](https://github.com/pypeclub/pype/issues/184) + +**Merged pull requests:** + +- Audio file existence check [\#614](https://github.com/pypeclub/pype/pull/614) +- NKS small fixes [\#587](https://github.com/pypeclub/pype/pull/587) +- Standalone publisher editorial plugins interfering [\#580](https://github.com/pypeclub/pype/pull/580) + +## [2.12.2](https://github.com/pypeclub/pype/tree/2.12.2) (2020-09-25) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.1...2.12.2) + +**Enhancements:** + +- pype config GUI [\#241](https://github.com/pypeclub/pype/issues/241) + +**Fixed bugs:** + +- Harmony: Saving heavy scenes will crash [\#507](https://github.com/pypeclub/pype/issues/507) +- Extract review a representation name with `\*\_burnin` [\#388](https://github.com/pypeclub/pype/issues/388) +- Hierarchy data was not considering active isntances [\#551](https://github.com/pypeclub/pype/pull/551) + +## [2.12.1](https://github.com/pypeclub/pype/tree/2.12.1) (2020-09-15) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.12.0...2.12.1) + +**Fixed bugs:** + +- Pype: changelog.md is outdated [\#503](https://github.com/pypeclub/pype/issues/503) +- dependency security alert ! [\#484](https://github.com/pypeclub/pype/issues/484) +- Maya: RenderSetup is missing update [\#106](https://github.com/pypeclub/pype/issues/106) +- \ extract effects creates new instance [\#78](https://github.com/pypeclub/pype/issues/78) + +## [2.12.0](https://github.com/pypeclub/pype/tree/2.12.0) (2020-09-10) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.8...2.12.0) + +**Enhancements:** + +- Less mongo connections [\#509](https://github.com/pypeclub/pype/pull/509) +- Nuke: adding image loader [\#499](https://github.com/pypeclub/pype/pull/499) +- Move launcher window to top if launcher action is clicked [\#450](https://github.com/pypeclub/pype/pull/450) +- Maya: better tile rendering support in Pype [\#446](https://github.com/pypeclub/pype/pull/446) +- Implementation of non QML launcher [\#443](https://github.com/pypeclub/pype/pull/443) +- Optional skip review on renders. [\#441](https://github.com/pypeclub/pype/pull/441) +- Ftrack: Option to push status from task to latest version [\#440](https://github.com/pypeclub/pype/pull/440) +- Properly containerize image plane loads. [\#434](https://github.com/pypeclub/pype/pull/434) +- Option to keep the review files. [\#426](https://github.com/pypeclub/pype/pull/426) +- Isolate view on instance members. [\#425](https://github.com/pypeclub/pype/pull/425) +- Maya: Publishing of tile renderings on Deadline [\#398](https://github.com/pypeclub/pype/pull/398) +- Feature/little bit better logging gui [\#383](https://github.com/pypeclub/pype/pull/383) + +**Fixed bugs:** + +- Maya: Fix tile order for Draft Tile Assembler [\#511](https://github.com/pypeclub/pype/pull/511) +- Remove extra dash [\#501](https://github.com/pypeclub/pype/pull/501) +- Fix: strip dot from repre names in single frame renders [\#498](https://github.com/pypeclub/pype/pull/498) +- Better handling of destination during integrating [\#485](https://github.com/pypeclub/pype/pull/485) +- Fix: allow thumbnail creation for single frame renders [\#460](https://github.com/pypeclub/pype/pull/460) +- added missing argument to launch\_application in ftrack app handler [\#453](https://github.com/pypeclub/pype/pull/453) +- Burnins: Copy bit rate of input video to match quality. [\#448](https://github.com/pypeclub/pype/pull/448) +- Standalone publisher is now independent from tray [\#442](https://github.com/pypeclub/pype/pull/442) +- Bugfix/empty enumerator attributes [\#436](https://github.com/pypeclub/pype/pull/436) +- Fixed wrong order of "other" category collapssing in publisher [\#435](https://github.com/pypeclub/pype/pull/435) +- Multiple reviews where being overwritten to one. [\#424](https://github.com/pypeclub/pype/pull/424) +- Cleanup plugin fail on instances without staging dir [\#420](https://github.com/pypeclub/pype/pull/420) +- deprecated -intra parameter in ffmpeg to new `-g` [\#417](https://github.com/pypeclub/pype/pull/417) +- Delivery action can now work with entered path [\#397](https://github.com/pypeclub/pype/pull/397) + +**Merged pull requests:** + +- Review on instance.data [\#473](https://github.com/pypeclub/pype/pull/473) + +## [2.11.8](https://github.com/pypeclub/pype/tree/2.11.8) (2020-08-27) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.7...2.11.8) + +**Enhancements:** + +- DWAA support for Maya [\#382](https://github.com/pypeclub/pype/issues/382) +- Isolate View on Playblast [\#367](https://github.com/pypeclub/pype/issues/367) +- Maya: Tile rendering [\#297](https://github.com/pypeclub/pype/issues/297) +- single pype instance running [\#47](https://github.com/pypeclub/pype/issues/47) +- PYPE-649: projects don't guarantee backwards compatible environment [\#8](https://github.com/pypeclub/pype/issues/8) +- PYPE-663: separate venv for each deployed version [\#7](https://github.com/pypeclub/pype/issues/7) + +**Fixed bugs:** + +- pyblish pype - other group is collapsed before plugins are done [\#431](https://github.com/pypeclub/pype/issues/431) +- Alpha white edges in harmony on PNGs [\#412](https://github.com/pypeclub/pype/issues/412) +- harmony image loader picks wrong representations [\#404](https://github.com/pypeclub/pype/issues/404) +- Clockify crash when response contain symbol not allowed by UTF-8 [\#81](https://github.com/pypeclub/pype/issues/81) + +## [2.11.7](https://github.com/pypeclub/pype/tree/2.11.7) (2020-08-21) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.6...2.11.7) + +**Fixed bugs:** + +- Clean Up Baked Movie [\#369](https://github.com/pypeclub/pype/issues/369) +- celaction last workfile [\#459](https://github.com/pypeclub/pype/pull/459) + +## [2.11.6](https://github.com/pypeclub/pype/tree/2.11.6) (2020-08-18) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.5...2.11.6) + +**Enhancements:** + +- publisher app [\#56](https://github.com/pypeclub/pype/issues/56) + +## [2.11.5](https://github.com/pypeclub/pype/tree/2.11.5) (2020-08-13) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.4...2.11.5) + +**Enhancements:** + +- Switch from master to equivalent [\#220](https://github.com/pypeclub/pype/issues/220) +- Standalone publisher now only groups sequence if the extension is known [\#439](https://github.com/pypeclub/pype/pull/439) + +**Fixed bugs:** + +- Logs have been disable for editorial by default to speed up publishing [\#433](https://github.com/pypeclub/pype/pull/433) +- additional fixes for celaction [\#430](https://github.com/pypeclub/pype/pull/430) +- Harmony: invalid variable scope in validate scene settings [\#428](https://github.com/pypeclub/pype/pull/428) +- new representation name for audio was not accepted [\#427](https://github.com/pypeclub/pype/pull/427) + +## [2.11.4](https://github.com/pypeclub/pype/tree/2.11.4) (2020-08-10) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.3...2.11.4) + +**Enhancements:** + +- WebSocket server [\#135](https://github.com/pypeclub/pype/issues/135) +- standalonepublisher: editorial family features expansion \[master branch\] [\#411](https://github.com/pypeclub/pype/pull/411) + +## [2.11.3](https://github.com/pypeclub/pype/tree/2.11.3) (2020-08-04) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.2...2.11.3) + +**Fixed bugs:** + +- Harmony: publishing performance issues [\#408](https://github.com/pypeclub/pype/pull/408) + +## [2.11.2](https://github.com/pypeclub/pype/tree/2.11.2) (2020-07-31) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.1...2.11.2) + +**Fixed bugs:** + +- Ftrack to Avalon bug [\#406](https://github.com/pypeclub/pype/issues/406) + +## [2.11.1](https://github.com/pypeclub/pype/tree/2.11.1) (2020-07-29) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.0...2.11.1) + +**Merged pull requests:** + +- Celaction: metadata json folder fixes on path [\#393](https://github.com/pypeclub/pype/pull/393) +- CelAction - version up method taken fro pype.lib [\#391](https://github.com/pypeclub/pype/pull/391) + ## 2.11.0 ## @@ -430,3 +787,6 @@ A large cleanup release. Most of the change are under the hood. - work directory was sometimes not being created correctly - major pype.lib cleanup. Removing of unused functions, merging those that were doing the same and general house cleaning. - _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* From 5f90dea9e5fb4f36fa5f1a80d8934c8cf2a397eb Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 24 Nov 2020 00:04:39 +0100 Subject: [PATCH 002/219] update changelog config --- .github_changelog_generator | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github_changelog_generator b/.github_changelog_generator index da6c35cebf..ac934ff84f 100644 --- a/.github_changelog_generator +++ b/.github_changelog_generator @@ -2,7 +2,8 @@ pr-wo-labels=False exclude-labels=duplicate,question,invalid,wontfix,weekly-digest author=False unreleased=True -since-tag=2.11.0 +since-tag=2.13.6 release-branch=master enhancement-label=**Enhancements:** issues=False +pulls=False From b29362411e2576fb8d871e2d750641ed491a11f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:32:12 +0100 Subject: [PATCH 003/219] add on/off to env value to bool --- pype/lib/env_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/env_tools.py b/pype/lib/env_tools.py index f31426103b..a5176b814d 100644 --- a/pype/lib/env_tools.py +++ b/pype/lib/env_tools.py @@ -20,9 +20,9 @@ def env_value_to_bool(env_key=None, value=None, default=False): if value is not None: value = str(value).lower() - if value in ("true", "yes", "1"): + if value in ("true", "yes", "1", "on"): return True - elif value in ("false", "no", "0"): + elif value in ("false", "no", "0", "off"): return False return default From 2164dbe6c18a7d9dccfaec79905e1232103dbf14 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:33:08 +0100 Subject: [PATCH 004/219] added to lib python functions for work with python modules and classes --- pype/lib/__init__.py | 10 ++++ pype/lib/python_module_tools.py | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 pype/lib/python_module_tools.py diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 188dd68039..426a5802c3 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -11,6 +11,12 @@ from .env_tools import ( get_paths_from_environ ) +from .python_module_tools import ( + modules_from_path, + recursive_bases_from_class, + classes_from_module +) + from .avalon_context import ( is_latest, any_outdated, @@ -53,6 +59,10 @@ __all__ = [ "env_value_to_bool", "get_paths_from_environ", + "modules_from_path", + "recursive_bases_from_class", + "classes_from_module", + "is_latest", "any_outdated", "get_asset", diff --git a/pype/lib/python_module_tools.py b/pype/lib/python_module_tools.py new file mode 100644 index 0000000000..61a3b1b09e --- /dev/null +++ b/pype/lib/python_module_tools.py @@ -0,0 +1,101 @@ +import os +import types +import inspect +import logging + +log = logging.getLogger(__name__) + + +def modules_from_path(folder_path): + """Get python scripts as modules from a path. + + Arguments: + path (str): Path to folder containing python scripts. + + Returns: + List of modules. + """ + + folder_path = os.path.normpath(folder_path) + + modules = [] + if not os.path.isdir(folder_path): + log.warning("Not a directory path: {}".format(folder_path)) + return modules + + for filename in os.listdir(folder_path): + # Ignore files which start with underscore + if filename.startswith("_"): + continue + + mod_name, mod_ext = os.path.splitext(filename) + if not mod_ext == ".py": + continue + + full_path = os.path.join(folder_path, filename) + if not os.path.isfile(full_path): + continue + + try: + # Prepare module object where content of file will be parsed + module = types.ModuleType(mod_name) + module.__file__ = full_path + + with open(full_path) as _stream: + # Execute content and store it to module object + exec(_stream.read(), module.__dict__) + + modules.append(module) + + except Exception: + log.warning( + "Failed to load path: \"{0}\"".format(full_path), + exc_info=True + ) + continue + + return modules + + +def recursive_bases_from_class(klass): + """Extract all bases from entered class.""" + result = [] + bases = klass.__bases__ + result.extend(bases) + for base in bases: + result.extend(recursive_bases_from_class(base)) + return result + + +def classes_from_module(superclass, module): + """Return plug-ins from module + + Arguments: + superclass (superclass): Superclass of subclasses to look for + module (types.ModuleType): Imported module from which to + parse valid Avalon plug-ins. + + Returns: + List of plug-ins, or empty list if none is found. + + """ + + classes = list() + for name in dir(module): + # It could be anything at this point + obj = getattr(module, name) + if not inspect.isclass(obj): + continue + + # These are subclassed from nothing, not even `object` + if not len(obj.__bases__) > 0: + continue + + # Use string comparison rather than `issubclass` + # in order to support reloading of this module. + bases = recursive_bases_from_class(obj) + if not any(base.__name__ == superclass.__name__ for base in bases): + continue + + classes.append(obj) + return classes From a3afd0b5cffe739136dec376aa574cc8846450e3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:33:56 +0100 Subject: [PATCH 005/219] applications has new class LaunchHook similar to PypeHook --- pype/lib/applications.py | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 600530a00f..8653e77da1 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -713,6 +713,74 @@ class Application: return self.manager.launch(self.app_name, *args, **kwargs) +@six.add_metaclass(ABCMeta) +class LaunchHook: + """Abstract class from all hooks should inherit.""" + # Order of prelaunch hook, will be executed as last if set to None. + order = None + # If hook should be executed befor or after application launch + prelaunch = True + # List of host implementations, skipped if empty. + hosts = [] + # List of platform availability, skipped if empty. + platforms = [] + + def __init__(self, launch_context): + """Constructor of launch hook. + + Always should be called + """ + self.log = logging.getLogger(self.__class__.__name__) + + self.launch_context = launch_context + + is_valid = self.class_validation(launch_context) + if is_valid: + is_valid = self.validate() + + self.is_valid = is_valid + + @classmethod + def class_validation(cls, launch_context): + """Validation of class attributes by launch context. + + Args: + launch_context (ApplicationLaunchContext): Context of launching + application. + + Returns: + bool: Is launch hook valid for the context by class attributes. + """ + if cls.platforms: + low_platforms = tuple( + _platform.lower() + for _platform in cls.platforms + ) + if platform.system().lower() not in low_platforms: + return False + + if cls.hosts: + if launch_context.host_name not in cls.hosts: + return False + + return True + + def validate(self): + """Optional validation of launch hook on initialization. + + Returns: + bool: Hook is valid (True) or invalid (False). + """ + # QUESTION Not sure if this method has any usable potential. + # - maybe result can be based on settings + return True + + @abstractmethod + def execute(self, *args, **kwargs): + """Abstract execute method where logic of hook is.""" + pass + + class ApplicationLaunchContext: """Context of launching application. From 52ff856bb65824c280a29fb5c4351f652f6142a2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:34:30 +0100 Subject: [PATCH 006/219] application context has method to load launch hooks --- pype/lib/applications.py | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 8653e77da1..0a6f7b481f 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -5,9 +5,12 @@ import getpass import json import copy import platform +import inspect import logging import subprocess +from abc import ABCMeta, abstractmethod +import six import acre import avalon.lib @@ -853,6 +856,78 @@ class ApplicationLaunchContext: self.prepare_host_environments() self.prepare_context_environments() + def paths_to_launch_hook(self): + """Directory paths where to look for launch hooks.""" + # This method has potential to be part of application manager (maybe). + paths = [] + + return paths + + def discover_launch_hooks(self): + classes = [] + paths = self.paths_to_launch_hook() + for path in paths: + if not os.path.exists(path): + self.log.info( + "Path to launch hooks does not exists: \"{}\"".format(path) + ) + continue + + modules = modules_from_path(path) + for _module in modules: + classes.extend(classes_from_module(LaunchHook, _module)) + + pre_hooks_with_order = [] + pre_hooks_without_order = [] + post_hooks_with_order = [] + post_hooks_without_order = [] + for klass in classes: + try: + hook = klass(self) + if not hook.is_valid: + self.log.debug( + "Hook is not valid for curent launch context." + ) + continue + + if inspect.isabstract(hook): + self.log.debug("Skipped abstract hook: {}".format( + str(hook) + )) + continue + + # Separate hooks if should be executed before or after launch + if hook.prelaunch: + if hook.order is None: + pre_hooks_without_order.append(hook) + else: + pre_hooks_with_order.append(hook) + else: + if hook.order is None: + post_hooks_with_order.append(hook) + else: + post_hooks_without_order.append(hook) + + except Exception: + self.log.warning( + "Initialization of hook failed. {}".format(str(klass)), + exc_info=True + ) + + # Sort hooks with order by order + pre_hooks_ordered = list(sorted( + pre_hooks_with_order, key=lambda obj: obj.order + )) + post_hooks_ordered = list(sorted( + post_hooks_with_order, key=lambda obj: obj.order + )) + + # Extend ordered hooks with hooks without defined order + pre_hooks_ordered.extend(pre_hooks_without_order) + post_hooks_ordered.extend(post_hooks_without_order) + + return pre_hooks_ordered, post_hooks_ordered + @property def app_name(self): return self.application.app_name From 17b05915870d8673ed228d2782e48d5b8879b992 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:43:28 +0100 Subject: [PATCH 007/219] added base of getting hooks dir paths --- pype/lib/applications.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 0a6f7b481f..b17b1fdbb0 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -23,6 +23,10 @@ from ..api import ( system_settings, environments ) +from .python_module_tools import ( + modules_from_path, + classes_from_module +) from .hooks import execute_hook from .deprecated import get_avalon_database from .env_tools import env_value_to_bool @@ -856,16 +860,28 @@ class ApplicationLaunchContext: self.prepare_host_environments() self.prepare_context_environments() - def paths_to_launch_hook(self): + def paths_to_launch_hooks(self): """Directory paths where to look for launch hooks.""" # This method has potential to be part of application manager (maybe). + + # TODO find better way how to define dir path to default launch hooks + import pype + pype_dir = os.path.dirname(os.path.abspath(pype.__file__)) + hooks_dir = os.path.join(pype_dir, "hooks") + + # TODO load additional studio paths from settings + # TODO add paths based on used modules (like `ftrack`) paths = [] - + subfolder_names = ["global", self.host_name, self.app_name] + for subfolder_name in subfolder_names: + path = os.path.join(hooks_dir, subfolder_name) + if os.path.exists(path) and os.path.isdir(path): + paths.append(path) return paths def discover_launch_hooks(self): classes = [] - paths = self.paths_to_launch_hook() + paths = self.paths_to_launch_hooks() for path in paths: if not os.path.exists(path): self.log.info( From e545a6798aa513818a370bb97d86d29a1fd0d63b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:46:22 +0100 Subject: [PATCH 008/219] prelaunch and post launch hooks are discovered and executed --- pype/lib/applications.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index b17b1fdbb0..b135db8057 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -968,20 +968,31 @@ class ApplicationLaunchContext: self.log.warning("Application was already launched.") return + # Discover launch hooks + prelaunch_hooks, postlaunch_hooks = self.discover_launch_hooks() + + # Execute prelaunch hooks + for prelaunch_hook in prelaunch_hooks: + prelaunch_hook.execute() + + # Prepare subprocess args args = self.clear_launch_args(self.launch_args) self.log.debug( "Launching \"{}\" with args: {}".format(self.app_name, args) ) + # Run process self.process = subprocess.Popen(args, **self.kwargs) - # TODO do this with after-launch hooks - try: - self.after_launch_procedures() - except Exception: - self.log.warning( - "After launch procedures were not successful.", - exc_info=True - ) + # Process post launch hooks + for postlaunch_hook in postlaunch_hooks: + try: + postlaunch_hook.execute() + + except Exception: + self.log.warning( + "After launch procedures were not successful.", + exc_info=True + ) return self.process From 0dc51f3d6d7fe05823c17eb0dfdef5f8cdd43648 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 14:51:32 +0100 Subject: [PATCH 009/219] hooks are launch hooks are stored to launch context --- pype/lib/applications.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index b135db8057..5aca9ac90b 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -853,6 +853,9 @@ class ApplicationLaunchContext: ) self.kwargs["creationflags"] = flags + self.prelaunch_hooks = None + self.postlaunch_hooks = None + self.process = None # TODO move these to pre-paunch hook @@ -879,7 +882,18 @@ class ApplicationLaunchContext: paths.append(path) return paths - def discover_launch_hooks(self): + def discover_launch_hooks(self, force=False): + if ( + self.prelaunch_hooks is not None + or self.postlaunch_hooks is not None + ): + if not force: + self.log.info("Launch hooks were already discovered.") + return + + self.prelaunch_hooks.clear() + self.postlaunch_hooks.clear() + classes = [] paths = self.paths_to_launch_hooks() for path in paths: @@ -942,7 +956,8 @@ class ApplicationLaunchContext: pre_hooks_ordered.extend(pre_hooks_without_order) post_hooks_ordered.extend(post_hooks_without_order) - return pre_hooks_ordered, post_hooks_ordered + self.prelaunch_hooks = pre_hooks_ordered + self.postlaunch_hooks = post_hooks_ordered @property def app_name(self): @@ -969,10 +984,10 @@ class ApplicationLaunchContext: return # Discover launch hooks - prelaunch_hooks, postlaunch_hooks = self.discover_launch_hooks() + self.discover_launch_hooks() # Execute prelaunch hooks - for prelaunch_hook in prelaunch_hooks: + for prelaunch_hook in self.prelaunch_hooks: prelaunch_hook.execute() # Prepare subprocess args @@ -984,7 +999,7 @@ class ApplicationLaunchContext: self.process = subprocess.Popen(args, **self.kwargs) # Process post launch hooks - for postlaunch_hook in postlaunch_hooks: + for postlaunch_hook in self.postlaunch_hooks: try: postlaunch_hook.execute() From 97c46f79c9134e7942c914e40f40a0d71665a63c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 15:41:19 +0100 Subject: [PATCH 010/219] few small changes --- pype/lib/__init__.py | 2 ++ pype/lib/applications.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 426a5802c3..512cb12e37 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -34,6 +34,7 @@ from .applications import ( ApplictionExecutableNotFound, ApplicationNotFound, ApplicationManager, + LaunchHook, launch_application, ApplicationAction, _subprocess @@ -78,6 +79,7 @@ __all__ = [ "ApplictionExecutableNotFound", "ApplicationNotFound", "ApplicationManager", + "LaunchHook", "launch_application", "ApplicationAction", diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 5aca9ac90b..ca80676cba 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -883,6 +883,7 @@ class ApplicationLaunchContext: return paths def discover_launch_hooks(self, force=False): + """Load and prepare launch hooks.""" if ( self.prelaunch_hooks is not None or self.postlaunch_hooks is not None @@ -1000,6 +1001,8 @@ class ApplicationLaunchContext: # Process post launch hooks for postlaunch_hook in self.postlaunch_hooks: + # TODO how to handle errors? + # - store to variable to let them accesible? try: postlaunch_hook.execute() From 3de491d487ee54141934d3378fedc6e84e19a7d8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 15:53:03 +0100 Subject: [PATCH 011/219] collect ftrack api module to context --- pype/plugins/ftrack/publish/collect_ftrack_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/ftrack/publish/collect_ftrack_api.py b/pype/plugins/ftrack/publish/collect_ftrack_api.py index bbda6da3b0..59839d7710 100644 --- a/pype/plugins/ftrack/publish/collect_ftrack_api.py +++ b/pype/plugins/ftrack/publish/collect_ftrack_api.py @@ -96,6 +96,7 @@ class CollectFtrackApi(pyblish.api.ContextPlugin): task_entity = None self.log.warning("Task name is not set.") + context.data["ftrackPythonModule"] = ftrack_api context.data["ftrackProject"] = project_entity context.data["ftrackEntity"] = asset_entity context.data["ftrackTask"] = task_entity From 3f869f20bb26168b7a9baf3b26a09be8c78e8956 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 15:53:38 +0100 Subject: [PATCH 012/219] removed backwards compatibility and added `get_pype_attr` to imports --- pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index e4496138bb..0e28fd1fe5 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -3,10 +3,10 @@ import six import pyblish.api from avalon import io -try: - from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_AUTO_SYNC -except Exception: - CUST_ATTR_AUTO_SYNC = "avalon_auto_sync" +from pype.modules.ftrack.lib.avalon_sync import ( + CUST_ATTR_AUTO_SYNC, + get_pype_attr +) class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): From 4ffda1a6466f2ff1932d98d2cb61049feec33fb5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 15:54:01 +0100 Subject: [PATCH 013/219] prepare hiearchical custom attributes definitions before import --- .../plugins/ftrack/publish/integrate_hierarchy_ftrack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index 0e28fd1fe5..c274669f55 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -73,6 +73,15 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): self.auto_sync_on(project) def import_to_ftrack(self, input_data, parent=None): + # Prequery hiearchical custom attributes + hier_custom_attributes = get_pype_attr(self.session)[1] + hier_attr_by_key = { + attr["key"]: attr + for attr in hier_custom_attributes + } + # Get ftrack api module (as they are different per python version) + ftrack_api = self.context.data["ftrackPythonModule"] + for entity_name in input_data: entity_data = input_data[entity_name] entity_type = entity_data['entity_type'] From 0a659548e9ad0206df7dd13aa1640891eeccc6fd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 15:54:29 +0100 Subject: [PATCH 014/219] use ftrack operations to set custom attribute value if key is hierarchical --- .../publish/integrate_hierarchy_ftrack.py | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index c274669f55..ef8ee9a216 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -1,5 +1,6 @@ import sys import six +import collections import pyblish.api from avalon import io @@ -124,12 +125,34 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): i for i in self.context if i.data['asset'] in entity['name'] ] for key in custom_attributes: - assert (key in entity['custom_attributes']), ( - 'Missing custom attribute key: `{0}` in attrs: ' - '`{1}`'.format(key, entity['custom_attributes'].keys()) - ) + hier_attr = hier_attr_by_key.get(key) + # Use simple method if key is not hierarchical + if not hier_attr: + assert (key in entity['custom_attributes']), ( + 'Missing custom attribute key: `{0}` in attrs: ' + '`{1}`'.format(key, entity['custom_attributes'].keys()) + ) - entity['custom_attributes'][key] = custom_attributes[key] + entity['custom_attributes'][key] = custom_attributes[key] + + else: + # Use ftrack operations method to set hiearchical + # attribute value. + # - this is because there may be non hiearchical custom + # attributes with different properties + entity_key = collections.OrderedDict({ + "configuration_id": hier_attr["id"], + "entity_id": entity["id"] + }) + self.session.recorded_operations.push( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + entity_key, + "value", + ftrack_api.symbol.NOT_SET, + custom_attributes[key] + ) + ) for instance in instances: instance.data['ftrackEntity'] = entity From 687ccf7217cea94fae380af0453ededb7afb7887 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:05:20 +0100 Subject: [PATCH 015/219] env attribute directly points to kwargs --- pype/lib/applications.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index ca80676cba..ed21a1b7e5 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -824,14 +824,6 @@ class ApplicationLaunchContext: self.data = dict(data) - # Handle launch environemtns - passed_env = self.data.pop("env", None) - if passed_env is None: - env = os.environ - else: - env = passed_env - self.env = copy.deepcopy(env) - # Load settings if were not passed in data settings_env = self.data.get("settings_env") if settings_env is None: @@ -840,9 +832,17 @@ class ApplicationLaunchContext: # subprocess.Popen launch arguments (first argument in constructor) self.launch_args = [executable] + + # Handle launch environemtns + passed_env = self.data.pop("env", None) + if passed_env is None: + env = os.environ + else: + env = passed_env + # subprocess.Popen keyword arguments self.kwargs = { - "env": self.env + "env": copy.deepcopy(env) } if platform.system().lower() == "windows": @@ -862,6 +862,24 @@ class ApplicationLaunchContext: self.prepare_global_data() self.prepare_host_environments() self.prepare_context_environments() + @property + def env(self): + if ( + "env" not in self.kwargs + or self.kwargs["env"] is None + ): + self.kwargs["env"] = {} + return self.kwargs["env"] + + @env.setter + def env(self, value): + if not isinstance(value, dict): + raise ValueError( + "'env' attribute expect 'dict' object. Got: {}".format( + str(type(value)) + ) + ) + self.kwargs["env"] = value def paths_to_launch_hooks(self): """Directory paths where to look for launch hooks.""" From 9a814320ba6952025fce0c2215214b2792e33432 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:05:46 +0100 Subject: [PATCH 016/219] LaunchHook is using PypeLogger --- pype/lib/applications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index ed21a1b7e5..80b698ecc8 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -737,7 +737,7 @@ class LaunchHook: Always should be called """ - self.log = logging.getLogger(self.__class__.__name__) + self.log = Logger().get_logger(self.__class__.__name__) self.launch_context = launch_context From baa10c6eb50fca4e5c61993774eed7857348e92b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:06:47 +0100 Subject: [PATCH 017/219] LaunchHook has few wrapped attributes --- pype/lib/applications.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 80b698ecc8..675b0c90be 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -772,6 +772,26 @@ class LaunchHook: return True + @property + def data(self): + return self.launch_context.data + + @property + def application(self): + return getattr(self.launch_context, "application", None) + + @property + def manager(self): + return getattr(self.application, "manager", None) + + @property + def host_name(self): + return getattr(self.application, "host_name", None) + + @property + def app_name(self): + return getattr(self.application, "app_name", None) + def validate(self): """Optional validation of launch hook on initialization. From e24ff6725a5ecc11df6d02d7f73e966b6752566b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:07:17 +0100 Subject: [PATCH 018/219] created global host launch hook --- pype/hooks/global/global_host_data.py | 366 ++++++++++++++++++++++++++ pype/lib/applications.py | 347 ++---------------------- 2 files changed, 384 insertions(+), 329 deletions(-) create mode 100644 pype/hooks/global/global_host_data.py diff --git a/pype/hooks/global/global_host_data.py b/pype/hooks/global/global_host_data.py new file mode 100644 index 0000000000..3f82f3b099 --- /dev/null +++ b/pype/hooks/global/global_host_data.py @@ -0,0 +1,366 @@ +import os +import re +import json +import getpass +import copy + +from pype.api import ( + Anatomy, + config +) +from pype.lib import ( + env_value_to_bool, + LaunchHook, + ApplicationLaunchFailed +) + +import acre +import avalon.api + + +class GlobalHostDataHook(LaunchHook): + order = -100 + + def execute(self): + """Prepare global objects to `data` that will be used for sure.""" + if not self.application.is_host: + self.log.info( + "Skipped hook {}. Application is not marked as host.".format( + self.__class__.__name__ + ) + ) + return + + self.prepare_global_data() + self.prepare_host_environments() + self.prepare_context_environments() + + def prepare_global_data(self): + """Prepare global objects to `data` that will be used for sure.""" + # Mongo documents + project_name = self.launch_context.data.get("project_name") + if not project_name: + self.log.info( + "Skipping global data preparation." + " Key `project_name` was not found in launch context." + ) + return + + self.log.debug("Project name is set to \"{}\"".format(project_name)) + # Anatomy + self.launch_context.data["anatomy"] = Anatomy(project_name) + + # Mongo connection + dbcon = avalon.api.AvalonMongoDB() + dbcon.Session["AVALON_PROJECT"] = project_name + dbcon.install() + + self.launch_context.data["dbcon"] = dbcon + + # Project document + project_doc = dbcon.find_one({"type": "project"}) + self.launch_context.data["project_doc"] = project_doc + + asset_name = self.launch_context.data.get("asset_name") + if not asset_name: + self.log.warning( + "Asset name was not set. Skipping asset document query." + ) + return + + asset_doc = dbcon.find_one({ + "type": "asset", + "name": asset_name + }) + self.launch_context.data["asset_doc"] = asset_doc + + def _merge_env(self, env, current_env): + """Modified function(merge) from acre module.""" + result = current_env.copy() + for key, value in env.items(): + # Keep missing keys by not filling `missing` kwarg + value = acre.lib.partial_format(value, data=current_env) + result[key] = value + return result + + def prepare_host_environments(self): + """Modify launch environments based on launched app and context.""" + # Keys for getting environments + env_keys = [ + self.launch_context.host_name, + self.launch_context.app_name + ] + + asset_doc = self.launch_context.data.get("asset_doc") + if asset_doc: + # Add tools environments + for key in asset_doc["data"].get("tools_env") or []: + tool = self.manager.tools.get(key) + if tool: + if tool.group_name not in env_keys: + env_keys.append(tool.group_name) + + if tool.name not in env_keys: + env_keys.append(tool.name) + + self.log.debug( + "Finding environment groups for keys: {}".format(env_keys) + ) + + settings_env = self.launch_context.data["settings_env"] + env_values = {} + for env_key in env_keys: + _env_values = settings_env.get(env_key) + if not _env_values: + continue + + # Choose right platform + tool_env = acre.parse(_env_values) + # Merge dictionaries + env_values = self._merge_env(tool_env, env_values) + + final_env = self._merge_env( + acre.compute(env_values), self.launch_context.env + ) + + # Update env + self.launch_context.env.update(final_env) + + def prepare_context_environments(self): + """Modify launch environemnts with context data for launched host.""" + # Context environments + project_doc = self.launch_context.data.get("project_doc") + asset_doc = self.launch_context.data.get("asset_doc") + task_name = self.launch_context.data.get("task_name") + if ( + not project_doc + or not asset_doc + or not task_name + ): + self.log.info( + "Skipping context environments preparation." + " Launch context does not contain required data." + ) + return + + workdir_data = self._prepare_workdir_data( + project_doc, asset_doc, task_name + ) + self.launch_context.data["workdir_data"] = workdir_data + + hierarchy = workdir_data["hierarchy"] + anatomy = self.launch_context.data["anatomy"] + + try: + anatomy_filled = anatomy.format(workdir_data) + workdir = os.path.normpath(anatomy_filled["work"]["folder"]) + if not os.path.exists(workdir): + self.log.debug( + "Creating workdir folder: \"{}\"".format(workdir) + ) + os.makedirs(workdir) + + except Exception as exc: + raise ApplicationLaunchFailed( + "Error in anatomy.format: {}".format(str(exc)) + ) + + context_env = { + "AVALON_PROJECT": project_doc["name"], + "AVALON_ASSET": asset_doc["name"], + "AVALON_TASK": task_name, + "AVALON_APP": self.launch_context.host_name, + "AVALON_APP_NAME": self.launch_context.app_name, + "AVALON_HIERARCHY": hierarchy, + "AVALON_WORKDIR": workdir + } + self.log.debug( + "Context environemnts set:\n{}".format( + json.dumps(context_env, indent=4) + ) + ) + self.launch_context.env.update(context_env) + + self.prepare_last_workfile(workdir) + + def _prepare_workdir_data(self, project_doc, asset_doc, task_name): + hierarchy = "/".join(asset_doc["data"]["parents"]) + + data = { + "project": { + "name": project_doc["name"], + "code": project_doc["data"].get("code") + }, + "task": task_name, + "asset": asset_doc["name"], + "app": self.launch_context.host_name, + "hierarchy": hierarchy + } + return data + + def prepare_last_workfile(self, workdir): + """last workfile workflow preparation. + + Function check if should care about last workfile workflow and tries + to find the last workfile. Both information are stored to `data` and + environments. + + Last workfile is filled always (with version 1) even if any workfile + exists yet. + + Args: + workdir (str): Path to folder where workfiles should be stored. + """ + _workdir_data = self.launch_context.data.get("workdir_data") + if not _workdir_data: + self.log.info( + "Skipping last workfile preparation." + " Key `workdir_data` not filled." + ) + return + + workdir_data = copy.deepcopy(_workdir_data) + project_name = self.launch_context.data["project_name"] + task_name = self.launch_context.data["task_name"] + start_last_workfile = self.should_start_last_workfile( + project_name, self.launch_context.host_name, task_name + ) + self.launch_context.data["start_last_workfile"] = start_last_workfile + + # Store boolean as "0"(False) or "1"(True) + self.launch_context.env["AVALON_OPEN_LAST_WORKFILE"] = ( + str(int(bool(start_last_workfile))) + ) + + _sub_msg = "" if start_last_workfile else " not" + self.log.debug( + "Last workfile should{} be opened on start.".format(_sub_msg) + ) + + # Last workfile path + last_workfile_path = "" + extensions = avalon.api.HOST_WORKFILE_EXTENSIONS.get( + self.launch_context.host_name + ) + if extensions: + anatomy = self.launch_context.data["anatomy"] + # Find last workfile + file_template = anatomy.templates["work"]["file"] + workdir_data.update({ + "version": 1, + "user": os.environ.get("PYPE_USERNAME") or getpass.getuser(), + "ext": extensions[0] + }) + + last_workfile_path = avalon.api.last_workfile( + workdir, file_template, workdir_data, extensions, True + ) + + if os.path.exists(last_workfile_path): + self.log.debug(( + "Workfiles for launch context does not exists" + " yet but path will be set." + )) + self.log.debug( + "Setting last workfile path: {}".format(last_workfile_path) + ) + + self.launch_context.env["AVALON_LAST_WORKFILE"] = last_workfile_path + self.launch_context.data["last_workfile_path"] = last_workfile_path + + def should_start_last_workfile(self, project_name, host_name, task_name): + """Define if host should start last version workfile if possible. + + Default output is `False`. Can be overriden with environment variable + `AVALON_OPEN_LAST_WORKFILE`, valid values without case sensitivity are + `"0", "1", "true", "false", "yes", "no"`. + + Args: + project_name (str): Name of project. + host_name (str): Name of host which is launched. In avalon's + application context it's value stored in app definition under + key `"application_dir"`. Is not case sensitive. + task_name (str): Name of task which is used for launching the host. + Task name is not case sensitive. + + Returns: + bool: True if host should start workfile. + + """ + default_output = env_value_to_bool( + "AVALON_OPEN_LAST_WORKFILE", default=False + ) + # TODO convert to settings + try: + startup_presets = ( + config.get_presets(project_name) + .get("tools", {}) + .get("workfiles", {}) + .get("last_workfile_on_startup") + ) + except Exception: + startup_presets = None + self.log.warning("Couldn't load pype's presets", exc_info=True) + + if not startup_presets: + return default_output + + host_name_lowered = host_name.lower() + task_name_lowered = task_name.lower() + + max_points = 2 + matching_points = -1 + matching_item = None + for item in startup_presets: + hosts = item.get("hosts") or tuple() + tasks = item.get("tasks") or tuple() + + hosts_lowered = tuple(_host_name.lower() for _host_name in hosts) + # Skip item if has set hosts and current host is not in + if hosts_lowered and host_name_lowered not in hosts_lowered: + continue + + tasks_lowered = tuple(_task_name.lower() for _task_name in tasks) + # Skip item if has set tasks and current task is not in + if tasks_lowered: + task_match = False + for task_regex in self.compile_list_of_regexes(tasks_lowered): + if re.match(task_regex, task_name_lowered): + task_match = True + break + + if not task_match: + continue + + points = int(bool(hosts_lowered)) + int(bool(tasks_lowered)) + if points > matching_points: + matching_item = item + matching_points = points + + if matching_points == max_points: + break + + if matching_item is not None: + output = matching_item.get("enabled") + if output is None: + output = default_output + return output + return default_output + + @staticmethod + def compile_list_of_regexes(in_list): + """Convert strings in entered list to compiled regex objects.""" + regexes = list() + if not in_list: + return regexes + + for item in in_list: + if item: + try: + regexes.append(re.compile(item)) + except TypeError: + print(( + "Invalid type \"{}\" value \"{}\"." + " Expected string based object. Skipping." + ).format(str(type(item)), str(item))) + return regexes diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 675b0c90be..3804db1ed1 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -29,7 +29,7 @@ from .python_module_tools import ( ) from .hooks import execute_hook from .deprecated import get_avalon_database -from .env_tools import env_value_to_bool + log = logging.getLogger(__name__) @@ -80,24 +80,6 @@ class ApplicationLaunchFailed(Exception): pass -def compile_list_of_regexes(in_list): - """Convert strings in entered list to compiled regex objects.""" - regexes = list() - if not in_list: - return regexes - - for item in in_list: - if item: - try: - regexes.append(re.compile(item)) - except TypeError: - print(( - "Invalid type \"{}\" value \"{}\"." - " Expected string based object. Skipping." - ).format(str(type(item)), str(item))) - return regexes - - def launch_application(project_name, asset_name, task_name, app_name): """Launch host application with filling required environments. @@ -878,10 +860,6 @@ class ApplicationLaunchContext: self.process = None - # TODO move these to pre-paunch hook - self.prepare_global_data() - self.prepare_host_environments() - self.prepare_context_environments() @property def env(self): if ( @@ -933,8 +911,14 @@ class ApplicationLaunchContext: self.prelaunch_hooks.clear() self.postlaunch_hooks.clear() - classes = [] + self.log.debug("Discovery of launch hooks started.") + paths = self.paths_to_launch_hooks() + self.log.debug("Paths where will look for launch hooks:{}".format( + "\n- ".join(paths) + )) + + classes = [] for path in paths: if not os.path.exists(path): self.log.info( @@ -997,6 +981,9 @@ class ApplicationLaunchContext: self.prelaunch_hooks = pre_hooks_ordered self.postlaunch_hooks = post_hooks_ordered + self.log.debug("Found {} prelaunch and {} postlaunch hooks.".format( + len(self.prelaunch_hooks), len(self.postlaunch_hooks) + )) @property def app_name(self): @@ -1027,6 +1014,9 @@ class ApplicationLaunchContext: # Execute prelaunch hooks for prelaunch_hook in self.prelaunch_hooks: + self.log.debug("Executing prelaunch hook: {}".format( + str(prelaunch_hook) + )) prelaunch_hook.execute() # Prepare subprocess args @@ -1039,6 +1029,10 @@ class ApplicationLaunchContext: # Process post launch hooks for postlaunch_hook in self.postlaunch_hooks: + self.log.debug("Executing postlaunch hook: {}".format( + str(prelaunch_hook) + )) + # TODO how to handle errors? # - store to variable to let them accesible? try: @@ -1087,311 +1081,6 @@ class ApplicationLaunchContext: break return args - def prepare_global_data(self): - """Prepare global objects to `data` that will be used for sure.""" - # Mongo documents - project_name = self.data.get("project_name") - if not project_name: - self.log.info( - "Skipping global data preparation." - " Key `project_name` was not found in launch context." - ) - return - - self.log.debug("Project name is set to \"{}\"".format(project_name)) - # Anatomy - self.data["anatomy"] = Anatomy(project_name) - - # Mongo connection - dbcon = avalon.api.AvalonMongoDB() - dbcon.Session["AVALON_PROJECT"] = project_name - dbcon.install() - - self.data["dbcon"] = dbcon - - # Project document - project_doc = dbcon.find_one({"type": "project"}) - self.data["project_doc"] = project_doc - - asset_name = self.data.get("asset_name") - if not asset_name: - self.log.warning( - "Asset name was not set. Skipping asset document query." - ) - return - - asset_doc = dbcon.find_one({ - "type": "asset", - "name": asset_name - }) - self.data["asset_doc"] = asset_doc - - def _merge_env(self, env, current_env): - """Modified function(merge) from acre module.""" - result = current_env.copy() - for key, value in env.items(): - # Keep missing keys by not filling `missing` kwarg - value = acre.lib.partial_format(value, data=current_env) - result[key] = value - return result - - def prepare_host_environments(self): - """Modify launch environments based on launched app and context.""" - # Keys for getting environments - env_keys = [self.host_name, self.app_name] - - asset_doc = self.data.get("asset_doc") - if asset_doc: - # Add tools environments - for key in asset_doc["data"].get("tools_env") or []: - tool = self.manager.tools.get(key) - if tool: - if tool.group_name not in env_keys: - env_keys.append(tool.group_name) - - if tool.name not in env_keys: - env_keys.append(tool.name) - - self.log.debug( - "Finding environment groups for keys: {}".format(env_keys) - ) - - settings_env = self.data["settings_env"] - env_values = {} - for env_key in env_keys: - _env_values = settings_env.get(env_key) - if not _env_values: - continue - - # Choose right platform - tool_env = acre.parse(_env_values) - # Merge dictionaries - env_values = self._merge_env(tool_env, env_values) - - final_env = self._merge_env(acre.compute(env_values), self.env) - - # Update env - self.env.update(final_env) - - def prepare_context_environments(self): - """Modify launch environemnts with context data for launched host.""" - # Context environments - project_doc = self.data.get("project_doc") - asset_doc = self.data.get("asset_doc") - task_name = self.data.get("task_name") - if ( - not project_doc - or not asset_doc - or not task_name - ): - self.log.info( - "Skipping context environments preparation." - " Launch context does not contain required data." - ) - return - - workdir_data = self._prepare_workdir_data( - project_doc, asset_doc, task_name - ) - self.data["workdir_data"] = workdir_data - - hierarchy = workdir_data["hierarchy"] - anatomy = self.data["anatomy"] - - try: - anatomy_filled = anatomy.format(workdir_data) - workdir = os.path.normpath(anatomy_filled["work"]["folder"]) - if not os.path.exists(workdir): - self.log.debug( - "Creating workdir folder: \"{}\"".format(workdir) - ) - os.makedirs(workdir) - - except Exception as exc: - raise ApplicationLaunchFailed( - "Error in anatomy.format: {}".format(str(exc)) - ) - - context_env = { - "AVALON_PROJECT": project_doc["name"], - "AVALON_ASSET": asset_doc["name"], - "AVALON_TASK": task_name, - "AVALON_APP": self.host_name, - "AVALON_APP_NAME": self.app_name, - "AVALON_HIERARCHY": hierarchy, - "AVALON_WORKDIR": workdir - } - self.log.debug( - "Context environemnts set:\n{}".format( - json.dumps(context_env, indent=4) - ) - ) - self.env.update(context_env) - - self.prepare_last_workfile(workdir) - - def _prepare_workdir_data(self, project_doc, asset_doc, task_name): - hierarchy = "/".join(asset_doc["data"]["parents"]) - - data = { - "project": { - "name": project_doc["name"], - "code": project_doc["data"].get("code") - }, - "task": task_name, - "asset": asset_doc["name"], - "app": self.host_name, - "hierarchy": hierarchy - } - return data - - def prepare_last_workfile(self, workdir): - """last workfile workflow preparation. - - Function check if should care about last workfile workflow and tries - to find the last workfile. Both information are stored to `data` and - environments. - - Last workfile is filled always (with version 1) even if any workfile - exists yet. - - Args: - workdir (str): Path to folder where workfiles should be stored. - """ - _workdir_data = self.data.get("workdir_data") - if not _workdir_data: - self.log.info( - "Skipping last workfile preparation." - " Key `workdir_data` not filled." - ) - return - - workdir_data = copy.deepcopy(_workdir_data) - project_name = self.data["project_name"] - task_name = self.data["task_name"] - start_last_workfile = self.should_start_last_workfile( - project_name, self.host_name, task_name - ) - self.data["start_last_workfile"] = start_last_workfile - - # Store boolean as "0"(False) or "1"(True) - self.env["AVALON_OPEN_LAST_WORKFILE"] = ( - str(int(bool(start_last_workfile))) - ) - - _sub_msg = "" if start_last_workfile else " not" - self.log.debug( - "Last workfile should{} be opened on start.".format(_sub_msg) - ) - - # Last workfile path - last_workfile_path = "" - extensions = avalon.api.HOST_WORKFILE_EXTENSIONS.get(self.host_name) - if extensions: - anatomy = self.data["anatomy"] - # Find last workfile - file_template = anatomy.templates["work"]["file"] - workdir_data.update({ - "version": 1, - "user": os.environ.get("PYPE_USERNAME") or getpass.getuser(), - "ext": extensions[0] - }) - - last_workfile_path = avalon.api.last_workfile( - workdir, file_template, workdir_data, extensions, True - ) - - if os.path.exists(last_workfile_path): - self.log.debug(( - "Workfiles for launch context does not exists" - " yet but path will be set." - )) - self.log.debug( - "Setting last workfile path: {}".format(last_workfile_path) - ) - - self.env["AVALON_LAST_WORKFILE"] = last_workfile_path - self.data["last_workfile_path"] = last_workfile_path - - def should_start_last_workfile(self, project_name, host_name, task_name): - """Define if host should start last version workfile if possible. - - Default output is `False`. Can be overriden with environment variable - `AVALON_OPEN_LAST_WORKFILE`, valid values without case sensitivity are - `"0", "1", "true", "false", "yes", "no"`. - - Args: - project_name (str): Name of project. - host_name (str): Name of host which is launched. In avalon's - application context it's value stored in app definition under - key `"application_dir"`. Is not case sensitive. - task_name (str): Name of task which is used for launching the host. - Task name is not case sensitive. - - Returns: - bool: True if host should start workfile. - - """ - default_output = env_value_to_bool( - "AVALON_OPEN_LAST_WORKFILE", default=False - ) - # TODO convert to settings - try: - startup_presets = ( - config.get_presets(project_name) - .get("tools", {}) - .get("workfiles", {}) - .get("last_workfile_on_startup") - ) - except Exception: - startup_presets = None - self.log.warning("Couldn't load pype's presets", exc_info=True) - - if not startup_presets: - return default_output - - host_name_lowered = host_name.lower() - task_name_lowered = task_name.lower() - - max_points = 2 - matching_points = -1 - matching_item = None - for item in startup_presets: - hosts = item.get("hosts") or tuple() - tasks = item.get("tasks") or tuple() - - hosts_lowered = tuple(_host_name.lower() for _host_name in hosts) - # Skip item if has set hosts and current host is not in - if hosts_lowered and host_name_lowered not in hosts_lowered: - continue - - tasks_lowered = tuple(_task_name.lower() for _task_name in tasks) - # Skip item if has set tasks and current task is not in - if tasks_lowered: - task_match = False - for task_regex in compile_list_of_regexes(tasks_lowered): - if re.match(task_regex, task_name_lowered): - task_match = True - break - - if not task_match: - continue - - points = int(bool(hosts_lowered)) + int(bool(tasks_lowered)) - if points > matching_points: - matching_item = item - matching_points = points - - if matching_points == max_points: - break - - if matching_item is not None: - output = matching_item.get("enabled") - if output is None: - output = default_output - return output - return default_output - def after_launch_procedures(self): self._ftrack_after_launch_procedure() From a1a48fbb7ae1ad04582519bb899200e99d44d480 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:30:30 +0100 Subject: [PATCH 019/219] moved ftrack logic from application context to post launch hook --- pype/hooks/global/post_ftrack_changes.py | 184 +++++++++++++++++++++++ pype/lib/applications.py | 167 -------------------- 2 files changed, 184 insertions(+), 167 deletions(-) create mode 100644 pype/hooks/global/post_ftrack_changes.py diff --git a/pype/hooks/global/post_ftrack_changes.py b/pype/hooks/global/post_ftrack_changes.py new file mode 100644 index 0000000000..6dfde5409a --- /dev/null +++ b/pype/hooks/global/post_ftrack_changes.py @@ -0,0 +1,184 @@ +import os + +import ftrack_api +from pype.api import config +from pype.lib import LaunchHook + + +class PostFtrackHook(LaunchHook): + order = None + + def execute(self): + project_name = self.data.get("project_name") + asset_name = self.data.get("asset_name") + task_name = self.data.get("task_name") + + missing_context_keys = set() + if not project_name: + missing_context_keys.add("project_name") + if not asset_name: + missing_context_keys.add("asset_name") + if not task_name: + missing_context_keys.add("task_name") + + if missing_context_keys: + missing_keys_str = ", ".join([ + "\"{}\"".format(key) for key in missing_context_keys + ]) + self.log.debug("Hook {} skipped. Missing data keys: {}".format( + self.__class__.__name__, missing_keys_str + )) + return + + required_keys = ("FTRACK_SERVER", "FTRACK_API_USER", "FTRACK_API_KEY") + for key in required_keys: + if not os.environ.get(key): + self.log.debug(( + "Missing required environment \"{}\"" + " for Ftrack after launch procedure." + ).format(key)) + return + + try: + session = ftrack_api.Session(auto_connect_event_hub=True) + self.log.debug("Ftrack session created") + except Exception: + self.log.warning("Couldn't create Ftrack session") + return + + try: + entity = self.find_ftrack_task_entity( + session, project_name, asset_name, task_name + ) + if entity: + self.ftrack_status_change(session, entity, project_name) + self.start_timer(session, entity, ftrack_api) + except Exception: + self.log.warning( + "Couldn't finish Ftrack procedure.", exc_info=True + ) + return + + finally: + session.close() + + def find_ftrack_task_entity( + self, session, project_name, asset_name, task_name + ): + project_entity = session.query( + "Project where full_name is \"{}\"".format(project_name) + ).first() + if not project_entity: + self.log.warning( + "Couldn't find project \"{}\" in Ftrack.".format(project_name) + ) + return + + potential_task_entities = session.query(( + "TypedContext where parent.name is \"{}\" and project_id is \"{}\"" + ).format(asset_name, project_entity["id"])).all() + filtered_entities = [] + for _entity in potential_task_entities: + if ( + _entity.entity_type.lower() == "task" + and _entity["name"] == task_name + ): + filtered_entities.append(_entity) + + if not filtered_entities: + self.log.warning(( + "Couldn't find task \"{}\" under parent \"{}\" in Ftrack." + ).format(task_name, asset_name)) + return + + if len(filtered_entities) > 1: + self.log.warning(( + "Found more than one task \"{}\"" + " under parent \"{}\" in Ftrack." + ).format(task_name, asset_name)) + return + + return filtered_entities[0] + + def ftrack_status_change(self, session, entity, project_name): + # TODO use settings + presets = config.get_presets(project_name)["ftrack"]["ftrack_config"] + statuses = presets.get("status_update") + if not statuses: + return + + actual_status = entity["status"]["name"].lower() + already_tested = set() + ent_path = "/".join( + [ent["name"] for ent in entity["link"]] + ) + while True: + next_status_name = None + for key, value in statuses.items(): + if key in already_tested: + continue + if actual_status in value or "_any_" in value: + if key != "_ignore_": + next_status_name = key + already_tested.add(key) + break + already_tested.add(key) + + if next_status_name is None: + break + + try: + query = "Status where name is \"{}\"".format( + next_status_name + ) + status = session.query(query).one() + + entity["status"] = status + session.commit() + self.log.debug("Changing status to \"{}\" <{}>".format( + next_status_name, ent_path + )) + break + + except Exception: + session.rollback() + msg = ( + "Status \"{}\" in presets wasn't found" + " on Ftrack entity type \"{}\"" + ).format(next_status_name, entity.entity_type) + self.log.warning(msg) + + def start_timer(self, session, entity, _ftrack_api): + """Start Ftrack timer on task from context.""" + self.log.debug("Triggering timer start.") + + user_entity = session.query("User where username is \"{}\"".format( + os.environ["FTRACK_API_USER"] + )).first() + if not user_entity: + self.log.warning( + "Couldn't find user with username \"{}\" in Ftrack".format( + os.environ["FTRACK_API_USER"] + ) + ) + return + + source = { + "user": { + "id": user_entity["id"], + "username": user_entity["username"] + } + } + event_data = { + "actionIdentifier": "start.timer", + "selection": [{"entityId": entity["id"], "entityType": "task"}] + } + session.event_hub.publish( + _ftrack_api.event.base.Event( + topic="ftrack.action.launch", + data=event_data, + source=source + ), + on_error="ignore" + ) + self.log.debug("Timer start triggered successfully.") diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 3804db1ed1..47cb1c7176 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -1080,170 +1080,3 @@ class ApplicationLaunchContext: if all_cleared: break return args - - def after_launch_procedures(self): - self._ftrack_after_launch_procedure() - - def _ftrack_after_launch_procedure(self): - # TODO move to launch hook - project_name = self.data.get("project_name") - asset_name = self.data.get("asset_name") - task_name = self.data.get("task_name") - if ( - not project_name - or not asset_name - or not task_name - ): - return - - required_keys = ("FTRACK_SERVER", "FTRACK_API_USER", "FTRACK_API_KEY") - for key in required_keys: - if not os.environ.get(key): - self.log.debug(( - "Missing required environment \"{}\"" - " for Ftrack after launch procedure." - ).format(key)) - return - - try: - import ftrack_api - session = ftrack_api.Session(auto_connect_event_hub=True) - self.log.debug("Ftrack session created") - except Exception: - self.log.warning("Couldn't create Ftrack session") - return - - try: - entity = self._find_ftrack_task_entity( - session, project_name, asset_name, task_name - ) - self._ftrack_status_change(session, entity, project_name) - self._start_timer(session, entity, ftrack_api) - except Exception: - self.log.warning( - "Couldn't finish Ftrack procedure.", exc_info=True - ) - return - - finally: - session.close() - - def _find_ftrack_task_entity( - self, session, project_name, asset_name, task_name - ): - project_entity = session.query( - "Project where full_name is \"{}\"".format(project_name) - ).first() - if not project_entity: - self.log.warning( - "Couldn't find project \"{}\" in Ftrack.".format(project_name) - ) - return - - potential_task_entities = session.query(( - "TypedContext where parent.name is \"{}\" and project_id is \"{}\"" - ).format(asset_name, project_entity["id"])).all() - filtered_entities = [] - for _entity in potential_task_entities: - if ( - _entity.entity_type.lower() == "task" - and _entity["name"] == task_name - ): - filtered_entities.append(_entity) - - if not filtered_entities: - self.log.warning(( - "Couldn't find task \"{}\" under parent \"{}\" in Ftrack." - ).format(task_name, asset_name)) - return - - if len(filtered_entities) > 1: - self.log.warning(( - "Found more than one task \"{}\"" - " under parent \"{}\" in Ftrack." - ).format(task_name, asset_name)) - return - - return filtered_entities[0] - - def _ftrack_status_change(self, session, entity, project_name): - from pype.api import config - presets = config.get_presets(project_name)["ftrack"]["ftrack_config"] - statuses = presets.get("status_update") - if not statuses: - return - - actual_status = entity["status"]["name"].lower() - already_tested = set() - ent_path = "/".join( - [ent["name"] for ent in entity["link"]] - ) - while True: - next_status_name = None - for key, value in statuses.items(): - if key in already_tested: - continue - if actual_status in value or "_any_" in value: - if key != "_ignore_": - next_status_name = key - already_tested.add(key) - break - already_tested.add(key) - - if next_status_name is None: - break - - try: - query = "Status where name is \"{}\"".format( - next_status_name - ) - status = session.query(query).one() - - entity["status"] = status - session.commit() - self.log.debug("Changing status to \"{}\" <{}>".format( - next_status_name, ent_path - )) - break - - except Exception: - session.rollback() - msg = ( - "Status \"{}\" in presets wasn't found" - " on Ftrack entity type \"{}\"" - ).format(next_status_name, entity.entity_type) - self.log.warning(msg) - - def _start_timer(self, session, entity, _ftrack_api): - self.log.debug("Triggering timer start.") - - user_entity = session.query("User where username is \"{}\"".format( - os.environ["FTRACK_API_USER"] - )).first() - if not user_entity: - self.log.warning( - "Couldn't find user with username \"{}\" in Ftrack".format( - os.environ["FTRACK_API_USER"] - ) - ) - return - - source = { - "user": { - "id": user_entity["id"], - "username": user_entity["username"] - } - } - event_data = { - "actionIdentifier": "start.timer", - "selection": [{"entityId": entity["id"], "entityType": "task"}] - } - session.event_hub.publish( - _ftrack_api.event.base.Event( - topic="ftrack.action.launch", - data=event_data, - source=source - ), - on_error="ignore" - ) - self.log.debug("Timer start triggered successfully.") From f97ae3f2690845426cb1856d248fb82e55084a5c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:30:48 +0100 Subject: [PATCH 020/219] renamed file to start with pre_* --- ...l_host_data.py => pre_global_host_data.py} | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) rename pype/hooks/global/{global_host_data.py => pre_global_host_data.py} (87%) diff --git a/pype/hooks/global/global_host_data.py b/pype/hooks/global/pre_global_host_data.py similarity index 87% rename from pype/hooks/global/global_host_data.py rename to pype/hooks/global/pre_global_host_data.py index 3f82f3b099..0b501212a0 100644 --- a/pype/hooks/global/global_host_data.py +++ b/pype/hooks/global/pre_global_host_data.py @@ -38,7 +38,7 @@ class GlobalHostDataHook(LaunchHook): def prepare_global_data(self): """Prepare global objects to `data` that will be used for sure.""" # Mongo documents - project_name = self.launch_context.data.get("project_name") + project_name = self.data.get("project_name") if not project_name: self.log.info( "Skipping global data preparation." @@ -48,20 +48,20 @@ class GlobalHostDataHook(LaunchHook): self.log.debug("Project name is set to \"{}\"".format(project_name)) # Anatomy - self.launch_context.data["anatomy"] = Anatomy(project_name) + self.data["anatomy"] = Anatomy(project_name) # Mongo connection dbcon = avalon.api.AvalonMongoDB() dbcon.Session["AVALON_PROJECT"] = project_name dbcon.install() - self.launch_context.data["dbcon"] = dbcon + self.data["dbcon"] = dbcon # Project document project_doc = dbcon.find_one({"type": "project"}) - self.launch_context.data["project_doc"] = project_doc + self.data["project_doc"] = project_doc - asset_name = self.launch_context.data.get("asset_name") + asset_name = self.data.get("asset_name") if not asset_name: self.log.warning( "Asset name was not set. Skipping asset document query." @@ -72,7 +72,7 @@ class GlobalHostDataHook(LaunchHook): "type": "asset", "name": asset_name }) - self.launch_context.data["asset_doc"] = asset_doc + self.data["asset_doc"] = asset_doc def _merge_env(self, env, current_env): """Modified function(merge) from acre module.""" @@ -86,12 +86,9 @@ class GlobalHostDataHook(LaunchHook): def prepare_host_environments(self): """Modify launch environments based on launched app and context.""" # Keys for getting environments - env_keys = [ - self.launch_context.host_name, - self.launch_context.app_name - ] + env_keys = [self.host_name, self.app_name] - asset_doc = self.launch_context.data.get("asset_doc") + asset_doc = self.data.get("asset_doc") if asset_doc: # Add tools environments for key in asset_doc["data"].get("tools_env") or []: @@ -107,7 +104,7 @@ class GlobalHostDataHook(LaunchHook): "Finding environment groups for keys: {}".format(env_keys) ) - settings_env = self.launch_context.data["settings_env"] + settings_env = self.data["settings_env"] env_values = {} for env_key in env_keys: _env_values = settings_env.get(env_key) @@ -129,9 +126,9 @@ class GlobalHostDataHook(LaunchHook): def prepare_context_environments(self): """Modify launch environemnts with context data for launched host.""" # Context environments - project_doc = self.launch_context.data.get("project_doc") - asset_doc = self.launch_context.data.get("asset_doc") - task_name = self.launch_context.data.get("task_name") + project_doc = self.data.get("project_doc") + asset_doc = self.data.get("asset_doc") + task_name = self.data.get("task_name") if ( not project_doc or not asset_doc @@ -146,10 +143,10 @@ class GlobalHostDataHook(LaunchHook): workdir_data = self._prepare_workdir_data( project_doc, asset_doc, task_name ) - self.launch_context.data["workdir_data"] = workdir_data + self.data["workdir_data"] = workdir_data hierarchy = workdir_data["hierarchy"] - anatomy = self.launch_context.data["anatomy"] + anatomy = self.data["anatomy"] try: anatomy_filled = anatomy.format(workdir_data) @@ -169,8 +166,8 @@ class GlobalHostDataHook(LaunchHook): "AVALON_PROJECT": project_doc["name"], "AVALON_ASSET": asset_doc["name"], "AVALON_TASK": task_name, - "AVALON_APP": self.launch_context.host_name, - "AVALON_APP_NAME": self.launch_context.app_name, + "AVALON_APP": self.host_name, + "AVALON_APP_NAME": self.app_name, "AVALON_HIERARCHY": hierarchy, "AVALON_WORKDIR": workdir } @@ -193,7 +190,7 @@ class GlobalHostDataHook(LaunchHook): }, "task": task_name, "asset": asset_doc["name"], - "app": self.launch_context.host_name, + "app": self.host_name, "hierarchy": hierarchy } return data @@ -211,7 +208,7 @@ class GlobalHostDataHook(LaunchHook): Args: workdir (str): Path to folder where workfiles should be stored. """ - _workdir_data = self.launch_context.data.get("workdir_data") + _workdir_data = self.data.get("workdir_data") if not _workdir_data: self.log.info( "Skipping last workfile preparation." @@ -220,12 +217,12 @@ class GlobalHostDataHook(LaunchHook): return workdir_data = copy.deepcopy(_workdir_data) - project_name = self.launch_context.data["project_name"] - task_name = self.launch_context.data["task_name"] + project_name = self.data["project_name"] + task_name = self.data["task_name"] start_last_workfile = self.should_start_last_workfile( - project_name, self.launch_context.host_name, task_name + project_name, self.host_name, task_name ) - self.launch_context.data["start_last_workfile"] = start_last_workfile + self.data["start_last_workfile"] = start_last_workfile # Store boolean as "0"(False) or "1"(True) self.launch_context.env["AVALON_OPEN_LAST_WORKFILE"] = ( @@ -240,10 +237,10 @@ class GlobalHostDataHook(LaunchHook): # Last workfile path last_workfile_path = "" extensions = avalon.api.HOST_WORKFILE_EXTENSIONS.get( - self.launch_context.host_name + self.host_name ) if extensions: - anatomy = self.launch_context.data["anatomy"] + anatomy = self.data["anatomy"] # Find last workfile file_template = anatomy.templates["work"]["file"] workdir_data.update({ @@ -266,7 +263,7 @@ class GlobalHostDataHook(LaunchHook): ) self.launch_context.env["AVALON_LAST_WORKFILE"] = last_workfile_path - self.launch_context.data["last_workfile_path"] = last_workfile_path + self.data["last_workfile_path"] = last_workfile_path def should_start_last_workfile(self, project_name, host_name, task_name): """Define if host should start last version workfile if possible. From 88316fe4211546d5290faf7204f58506e1ff4264 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 17:56:58 +0100 Subject: [PATCH 021/219] prelaunch and postlaunch hooks are diferentiated by inherit class --- pype/hooks/global/post_ftrack_changes.py | 4 +- pype/hooks/global/pre_global_host_data.py | 4 +- pype/lib/__init__.py | 6 +- pype/lib/applications.py | 127 +++++++++++++--------- 4 files changed, 82 insertions(+), 59 deletions(-) diff --git a/pype/hooks/global/post_ftrack_changes.py b/pype/hooks/global/post_ftrack_changes.py index 6dfde5409a..144f618620 100644 --- a/pype/hooks/global/post_ftrack_changes.py +++ b/pype/hooks/global/post_ftrack_changes.py @@ -2,10 +2,10 @@ import os import ftrack_api from pype.api import config -from pype.lib import LaunchHook +from pype.lib import PostLaunchHook -class PostFtrackHook(LaunchHook): +class PostFtrackHook(PostLaunchHook): order = None def execute(self): diff --git a/pype/hooks/global/pre_global_host_data.py b/pype/hooks/global/pre_global_host_data.py index 0b501212a0..787460019d 100644 --- a/pype/hooks/global/pre_global_host_data.py +++ b/pype/hooks/global/pre_global_host_data.py @@ -10,7 +10,7 @@ from pype.api import ( ) from pype.lib import ( env_value_to_bool, - LaunchHook, + PreLaunchHook, ApplicationLaunchFailed ) @@ -18,7 +18,7 @@ import acre import avalon.api -class GlobalHostDataHook(LaunchHook): +class GlobalHostDataHook(PreLaunchHook): order = -100 def execute(self): diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 512cb12e37..ecdd155c99 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -34,7 +34,8 @@ from .applications import ( ApplictionExecutableNotFound, ApplicationNotFound, ApplicationManager, - LaunchHook, + PreLaunchHook, + PostLaunchHook, launch_application, ApplicationAction, _subprocess @@ -79,7 +80,8 @@ __all__ = [ "ApplictionExecutableNotFound", "ApplicationNotFound", "ApplicationManager", - "LaunchHook", + "PreLaunchHook", + "PostLaunchHook", "launch_application", "ApplicationAction", diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 47cb1c7176..9d34a8b22c 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -704,11 +704,9 @@ class Application: @six.add_metaclass(ABCMeta) class LaunchHook: - """Abstract class from all hooks should inherit.""" + """Abstract base class of launch hook.""" # Order of prelaunch hook, will be executed as last if set to None. order = None - # If hook should be executed befor or after application launch - prelaunch = True # List of host implementations, skipped if empty. hosts = [] # List of platform availability, skipped if empty. @@ -790,6 +788,26 @@ class LaunchHook: pass +class PreLaunchHook(LaunchHook): + """Abstract class of prelaunch hook. + + This launch hook will be processed before application is launched. + + If any exception will happen during processing the application won't be + launched. + """ + + +class PostLaunchHook(LaunchHook): + """Abstract class of postlaunch hook. + + This launch hook will be processed after application is launched. + + Nothing will happen if any exception will happen during processing. And + processing of other postlaunch hooks won't stop either. + """ + + class ApplicationLaunchContext: """Context of launching application. @@ -918,7 +936,10 @@ class ApplicationLaunchContext: "\n- ".join(paths) )) - classes = [] + all_classes = { + "pre": [], + "post": [] + } for path in paths: if not os.path.exists(path): self.log.info( @@ -928,59 +949,55 @@ class ApplicationLaunchContext: modules = modules_from_path(path) for _module in modules: - classes.extend(classes_from_module(LaunchHook, _module)) - - pre_hooks_with_order = [] - pre_hooks_without_order = [] - post_hooks_with_order = [] - post_hooks_without_order = [] - for klass in classes: - try: - hook = klass(self) - if not hook.is_valid: - self.log.debug( - "Hook is not valid for curent launch context." - ) - continue - - if inspect.isabstract(hook): - self.log.debug("Skipped abstract hook: {}".format( - str(hook) - )) - continue - - # Separate hooks if should be executed before or after launch - if hook.prelaunch: - if hook.order is None: - pre_hooks_without_order.append(hook) - else: - pre_hooks_with_order.append(hook) - else: - if hook.order is None: - post_hooks_with_order.append(hook) - else: - post_hooks_without_order.append(hook) - - except Exception: - self.log.warning( - "Initialization of hook failed. {}".format(str(klass)), - exc_info=True + all_classes["pre"].extend( + classes_from_module(PreLaunchHook, _module) + ) + all_classes["post"].extend( + classes_from_module(PostLaunchHook, _module) ) - # Sort hooks with order by order - pre_hooks_ordered = list(sorted( - pre_hooks_with_order, key=lambda obj: obj.order - )) - post_hooks_ordered = list(sorted( - post_hooks_with_order, key=lambda obj: obj.order - )) + for launch_type, classes in all_classes.items(): + hooks_with_order = [] + hooks_without_order = [] + for klass in classes: + try: + hook = klass(self) + if not hook.is_valid: + self.log.debug( + "Hook is not valid for curent launch context." + ) + continue - # Extend ordered hooks with hooks without defined order - pre_hooks_ordered.extend(pre_hooks_without_order) - post_hooks_ordered.extend(post_hooks_without_order) + if inspect.isabstract(hook): + self.log.debug("Skipped abstract hook: {}".format( + str(hook) + )) + continue + + # Separate hooks by pre/post class + if hook.order is None: + hooks_without_order.append(hook) + else: + hooks_with_order.append(hook) + + except Exception: + self.log.warning( + "Initialization of hook failed. {}".format(str(klass)), + exc_info=True + ) + + # Sort hooks with order by order + ordered_hooks = list(sorted( + hooks_with_order, key=lambda obj: obj.order + )) + # Extend ordered hooks with hooks without defined order + ordered_hooks.extend(hooks_without_order) + + if launch_type == "pre": + self.prelaunch_hooks = ordered_hooks + else: + self.postlaunch_hooks = ordered_hooks - self.prelaunch_hooks = pre_hooks_ordered - self.postlaunch_hooks = post_hooks_ordered self.log.debug("Found {} prelaunch and {} postlaunch hooks.".format( len(self.prelaunch_hooks), len(self.postlaunch_hooks) )) @@ -1019,6 +1036,8 @@ class ApplicationLaunchContext: )) prelaunch_hook.execute() + self.log.debug("All prelaunch hook executed. Starting new process.") + # Prepare subprocess args args = self.clear_launch_args(self.launch_args) self.log.debug( @@ -1044,6 +1063,8 @@ class ApplicationLaunchContext: exc_info=True ) + self.log.debug("Launch of {} finished.".format(self.app_name)) + return self.process @staticmethod From 8204382719d3e2d5479dfcdd5718a78ab7d6f9f8 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 24 Nov 2020 18:13:09 +0100 Subject: [PATCH 022/219] fix multiple camera prefix --- pype/hosts/maya/expected_files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index 8d225bc13d..7324f1cfd0 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -266,8 +266,8 @@ class AExpectedFiles: def _generate_single_file_sequence(self, layer_data): expected_files = [] - file_prefix = layer_data["filePrefix"] for cam in layer_data["cameras"]: + file_prefix = layer_data["filePrefix"] mappings = ( (R_SUBSTITUTE_SCENE_TOKEN, layer_data["sceneName"]), (R_SUBSTITUTE_LAYER_TOKEN, layer_data["layerName"]), @@ -299,9 +299,9 @@ class AExpectedFiles: def _generate_aov_file_sequences(self, layer_data): expected_files = [] aov_file_list = {} - file_prefix = layer_data["filePrefix"] for aov in layer_data["enabledAOVs"]: for cam in layer_data["cameras"]: + file_prefix = layer_data["filePrefix"] mappings = ( (R_SUBSTITUTE_SCENE_TOKEN, layer_data["sceneName"]), From b1c587ae8347d97ba96e202dad219edc1cacc388 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 18:18:39 +0100 Subject: [PATCH 023/219] added few basic prelaunch hooks for nuke --- pype/hooks/hiero/pre_launch_args.py | 18 ++++++++++++++++++ pype/hooks/nukestudio/pre_launch_args.py | 18 ++++++++++++++++++ pype/hooks/nukex/pre_launch_args.py | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 pype/hooks/hiero/pre_launch_args.py create mode 100644 pype/hooks/nukestudio/pre_launch_args.py create mode 100644 pype/hooks/nukex/pre_launch_args.py diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py new file mode 100644 index 0000000000..20bac0ca8e --- /dev/null +++ b/pype/hooks/hiero/pre_launch_args.py @@ -0,0 +1,18 @@ +from pype.lib import PreLaunchHook + + +class HieroLaunchArguments(PreLaunchHook): + order = 0 + + def execute(self): + """Prepare suprocess launch arguments for NukeX.""" + # Get executable + executable = self.launch_context.launch_args[0] + + if isinstance(executable, str): + executable = [executable] + + # Add `nukex` argument and make sure it's bind to execuable + executable.append("--hiero") + + self.launch_context.launch_args[0] = executable diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py new file mode 100644 index 0000000000..a1c8dda314 --- /dev/null +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -0,0 +1,18 @@ +from pype.lib import PreLaunchHook + + +class NukeStudioLaunchArguments(PreLaunchHook): + order = 0 + + def execute(self): + """Prepare suprocess launch arguments for NukeX.""" + # Get executable + executable = self.launch_context.launch_args[0] + + if isinstance(executable, str): + executable = [executable] + + # Add `nukex` argument and make sure it's bind to execuable + executable.append("--studio") + + self.launch_context.launch_args[0] = executable diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py new file mode 100644 index 0000000000..b204cf0cbb --- /dev/null +++ b/pype/hooks/nukex/pre_launch_args.py @@ -0,0 +1,18 @@ +from pype.lib import PreLaunchHook + + +class NukeXLaunchArguments(PreLaunchHook): + order = 0 + + def execute(self): + """Prepare suprocess launch arguments for NukeX.""" + # Get executable + executable = self.launch_context.launch_args[0] + + if isinstance(executable, str): + executable = [executable] + + # Add `nukex` argument and make sure it's bind to execuable + executable.append("--nukex") + + self.launch_context.launch_args[0] = executable From 8abd04959ad7b7e3bc9f8140c2073ae84c9c6f61 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 18:25:26 +0100 Subject: [PATCH 024/219] added prelaunch hook for tvpaint for installation of pywin32 --- pype/hooks/tvpaint/pre_install_pywin.py | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 pype/hooks/tvpaint/pre_install_pywin.py diff --git a/pype/hooks/tvpaint/pre_install_pywin.py b/pype/hooks/tvpaint/pre_install_pywin.py new file mode 100644 index 0000000000..20d4b2aae7 --- /dev/null +++ b/pype/hooks/tvpaint/pre_install_pywin.py @@ -0,0 +1,34 @@ +from pype.lib import ( + PreLaunchHook, + ApplicationLaunchFailed, + _subprocess +) + + +class PreInstallPyWin(PreLaunchHook): + """Hook makes sure there is installed python module pywin32 on windows.""" + # WARNING This hook will probably be deprecated in Pype 3 - kept for test + order = 10 + hosts = ["tvpaint"] + platforms = ["windows"] + + def execute(self): + installed = False + try: + from win32com.shell import shell + self.log.debug("Python module `pywin32` already installed.") + installed = True + except Exception: + pass + + if installed: + return + + try: + output = _subprocess( + ["pip", "install", "pywin32==227"], logger=self.log + ) + except RuntimeError: + msg = "Installation of python module `pywin32` crashed." + self.log.warning(msg, exc_info=True) + raise ApplicationLaunchFailed(msg) From b9c569c159a824489832dfea5fff5b5fca8dac6a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 18:59:33 +0100 Subject: [PATCH 025/219] added hosts filtering --- pype/hooks/hiero/pre_launch_args.py | 3 ++- pype/hooks/nukestudio/pre_launch_args.py | 1 + pype/hooks/nukex/pre_launch_args.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index 20bac0ca8e..ff5e3d79e5 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -3,7 +3,8 @@ from pype.lib import PreLaunchHook class HieroLaunchArguments(PreLaunchHook): order = 0 - + hosts = ["hiero"] + def execute(self): """Prepare suprocess launch arguments for NukeX.""" # Get executable diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index a1c8dda314..b0ca41a614 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -3,6 +3,7 @@ from pype.lib import PreLaunchHook class NukeStudioLaunchArguments(PreLaunchHook): order = 0 + hosts = ["nukestudio"] def execute(self): """Prepare suprocess launch arguments for NukeX.""" diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index b204cf0cbb..16a83cfd63 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -3,6 +3,7 @@ from pype.lib import PreLaunchHook class NukeXLaunchArguments(PreLaunchHook): order = 0 + hosts = ["nukex"] def execute(self): """Prepare suprocess launch arguments for NukeX.""" From 163c2d7e7dc0e01cb8fb4347366fd7456936da09 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 19:00:01 +0100 Subject: [PATCH 026/219] implemented tvpaint prelaunch hook which modify launch arguments --- pype/hooks/tvpaint/pre_launch_args.py | 99 +++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 pype/hooks/tvpaint/pre_launch_args.py diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py new file mode 100644 index 0000000000..9f90398433 --- /dev/null +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -0,0 +1,99 @@ +import os +import shutil + +from pype.hosts import tvpaint +from pype.lib import ( + PreLaunchHook, + ApplicationLaunchFailed, + _subprocess +) +import avalon + + +class TvpaintPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to tvpaint implementation before + tvpaint executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + hosts = ["tvpaint"] + + def execute(self): + tvpaint_executable = self.launch_context.launch_args.pop(0) + + # This should never be used! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + self.launch_context.launch_args.append( + self.main_executable() + ) + self.launch_context.launch_args.append( + "\"{}\"".format(self.launch_script_path()) + ) + self.launch_context.launch_args.append( + "\"{}\"".format(tvpaint_executable) + ) + + # Add workfile to launch arguments + workfile_path = self.workfile_path() + if workfile_path: + self.launch_context.launch_args.append( + "\"{}\"".format(workfile_path) + ) + + if remainders: + self.log.warning(( + "There are unexpected launch arguments in TVPaint launch. {}" + ).format(str(remainders))) + self.launch_context.launch_args.extend(remainders) + + def main_executable(self): + """Should lead to python executable.""" + # TODO change in Pype 3 + return os.environ["PYPE_PYTHON_EXE"] + + def launch_script_path(self): + avalon_dir = os.path.dirname(os.path.abspath(avalon.__file__)) + script_path = os.path.join( + avalon_dir, + "tvpaint", + "launch_script.py" + ) + return script_path + + def workfile_path(self): + workfile_path = self.data["last_workfile"] + + # copy workfile from template if doesnt exist any on path + if not os.path.exists(workfile_path): + # TODO add ability to set different template workfile path via + # settings + pype_dir = os.path.dirname(os.path.abspath(tvpaint.__file__)) + template_path = os.path.join(pype_dir, "template.tvpp") + + if not os.path.exists(template_path): + self.log.warning( + "Couldn't find workfile template file in {}".format( + template_path + ) + ) + return + + self.log.info( + f"Creating workfile from template: \"{template_path}\"" + ) + + # Copy template workfile to new destinantion + shutil.copy2( + os.path.normpath(template_path), + os.path.normpath(workfile_path) + ) + + self.log.info(f"Workfile to open: \"{workfile_path}\"") + + return workfile_path From 1e91b84017783ebe5304880037945070b15dd444 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 19:25:44 +0100 Subject: [PATCH 027/219] add last workfile if should start last workfile --- pype/hooks/hiero/pre_launch_args.py | 10 +++++++++- pype/hooks/nukestudio/pre_launch_args.py | 8 ++++++++ pype/hooks/nukex/pre_launch_args.py | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index ff5e3d79e5..cb03f03b88 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -1,10 +1,11 @@ +import os from pype.lib import PreLaunchHook class HieroLaunchArguments(PreLaunchHook): order = 0 hosts = ["hiero"] - + def execute(self): """Prepare suprocess launch arguments for NukeX.""" # Get executable @@ -17,3 +18,10 @@ class HieroLaunchArguments(PreLaunchHook): executable.append("--hiero") self.launch_context.launch_args[0] = executable + + if self.data.get("start_last_workfile"): + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.launch_context.launch_args.append( + "\"{}\"".format(last_workfile) + ) diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index b0ca41a614..d567f36ad0 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -1,3 +1,4 @@ +import os from pype.lib import PreLaunchHook @@ -17,3 +18,10 @@ class NukeStudioLaunchArguments(PreLaunchHook): executable.append("--studio") self.launch_context.launch_args[0] = executable + + if self.data.get("start_last_workfile"): + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.launch_context.launch_args.append( + "\"{}\"".format(last_workfile) + ) diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index 16a83cfd63..eb0b963926 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -1,3 +1,4 @@ +import os from pype.lib import PreLaunchHook @@ -17,3 +18,10 @@ class NukeXLaunchArguments(PreLaunchHook): executable.append("--nukex") self.launch_context.launch_args[0] = executable + + if self.data.get("start_last_workfile"): + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.launch_context.launch_args.append( + "\"{}\"".format(last_workfile) + ) From cb07c41d92ef3cf68faafa596552ac18223b247c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 20:01:07 +0100 Subject: [PATCH 028/219] added few prelaunch hooks --- .../celaction/pre_celaction_registers.py | 128 ++++++++++++++++++ pype/hooks/fusion/pre_fusion_setup.py | 50 +++++++ pype/hooks/photoshop/pre_launch_args.py | 53 ++++++++ pype/hooks/tvpaint/pre_launch_args.py | 20 +-- 4 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 pype/hooks/celaction/pre_celaction_registers.py create mode 100644 pype/hooks/fusion/pre_fusion_setup.py create mode 100644 pype/hooks/photoshop/pre_launch_args.py diff --git a/pype/hooks/celaction/pre_celaction_registers.py b/pype/hooks/celaction/pre_celaction_registers.py new file mode 100644 index 0000000000..c638ce3c0b --- /dev/null +++ b/pype/hooks/celaction/pre_celaction_registers.py @@ -0,0 +1,128 @@ +import os +import shutil +import winreg +from pype.lib import PreLaunchHook +from pype.hosts import celaction + + +class CelactionPrelaunchHook(PreLaunchHook): + """ + This hook will check if current workfile path has Unreal + project inside. IF not, it initialize it and finally it pass + path to the project by environment variable to Unreal launcher + shell script. + """ + workfile_ext = "scn" + hosts = ["celaction"] + platforms = ["windows"] + + def execute(self): + # Add workfile path to launch arguments + workfile_path = self.workfile_path() + if workfile_path: + self.launch_context.launch_args.append( + "\"{}\"".format(workfile_path) + ) + + project_name = self.data["project_name"] + asset_name = self.data["asset_name"] + task_name = self.data["task_name"] + + # get publish version of celaction + app = "celaction_publish" + + # setting output parameters + path = r"Software\CelAction\CelAction2D\User Settings" + winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) + hKey = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, + "Software\\CelAction\\CelAction2D\\User Settings", 0, + winreg.KEY_ALL_ACCESS) + + # TODO: change to root path and pyblish standalone to premiere way + pype_root_path = os.getenv("PYPE_SETUP_PATH") + path = os.path.join(pype_root_path, + "pype.bat") + + winreg.SetValueEx(hKey, "SubmitAppTitle", 0, winreg.REG_SZ, path) + + parameters = [ + "launch", + f"--app {app}", + f"--project {project_name}", + f"--asset {asset_name}", + f"--task {task_name}", + "--currentFile \\\"\"*SCENE*\"\\\"", + "--chunk 10", + "--frameStart *START*", + "--frameEnd *END*", + "--resolutionWidth *X*", + "--resolutionHeight *Y*", + # "--programDir \"'*PROGPATH*'\"" + ] + winreg.SetValueEx(hKey, "SubmitParametersTitle", 0, winreg.REG_SZ, + " ".join(parameters)) + + # setting resolution parameters + path = r"Software\CelAction\CelAction2D\User Settings\Dialogs" + path += r"\SubmitOutput" + winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) + hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, + winreg.KEY_ALL_ACCESS) + winreg.SetValueEx(hKey, "SaveScene", 0, winreg.REG_DWORD, 1) + winreg.SetValueEx(hKey, "CustomX", 0, winreg.REG_DWORD, 1920) + winreg.SetValueEx(hKey, "CustomY", 0, winreg.REG_DWORD, 1080) + + # making sure message dialogs don't appear when overwriting + path = r"Software\CelAction\CelAction2D\User Settings\Messages" + path += r"\OverwriteScene" + winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) + hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, + winreg.KEY_ALL_ACCESS) + winreg.SetValueEx(hKey, "Result", 0, winreg.REG_DWORD, 6) + winreg.SetValueEx(hKey, "Valid", 0, winreg.REG_DWORD, 1) + + path = r"Software\CelAction\CelAction2D\User Settings\Messages" + path += r"\SceneSaved" + winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) + hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, + winreg.KEY_ALL_ACCESS) + winreg.SetValueEx(hKey, "Result", 0, winreg.REG_DWORD, 1) + winreg.SetValueEx(hKey, "Valid", 0, winreg.REG_DWORD, 1) + + def workfile_path(self): + workfile_path = self.data["last_workfile"] + + # copy workfile from template if doesnt exist any on path + if not os.path.exists(workfile_path): + # TODO add ability to set different template workfile path via + # settings + pype_celaction_dir = os.path.dirname( + os.path.abspath(celaction.__file__) + ) + template_path = os.path.join( + pype_celaction_dir, + "celaction_template_scene.scn" + ) + + if not os.path.exists(template_path): + self.log.warning( + "Couldn't find workfile template file in {}".format( + template_path + ) + ) + return + + self.log.info( + f"Creating workfile from template: \"{template_path}\"" + ) + + # Copy template workfile to new destinantion + shutil.copy2( + os.path.normpath(template_path), + os.path.normpath(workfile_path) + ) + + self.log.info(f"Workfile to open: \"{workfile_path}\"") + + return workfile_path diff --git a/pype/hooks/fusion/pre_fusion_setup.py b/pype/hooks/fusion/pre_fusion_setup.py new file mode 100644 index 0000000000..ac7dda4250 --- /dev/null +++ b/pype/hooks/fusion/pre_fusion_setup.py @@ -0,0 +1,50 @@ +import os +import importlib +from pype.lib import PreLaunchHook +from pype.hosts.fusion import utils + + +class FusionPrelaunch(PreLaunchHook): + """ + This hook will check if current workfile path has Fusion + project inside. + """ + hosts = ["fusion"] + + def execute(self): + # making sure pyton 3.6 is installed at provided path + py36_dir = os.path.normpath(self.env.get("PYTHON36", "")) + assert os.path.isdir(py36_dir), ( + "Python 3.6 is not installed at the provided folder path. Either " + "make sure the `environments\resolve.json` is having correctly " + "set `PYTHON36` or make sure Python 3.6 is installed " + f"in given path. \nPYTHON36E: `{py36_dir}`" + ) + self.log.info(f"Path to Fusion Python folder: `{py36_dir}`...") + self.env["PYTHON36"] = py36_dir + + # setting utility scripts dir for scripts syncing + us_dir = os.path.normpath( + self.env.get("FUSION_UTILITY_SCRIPTS_DIR", "") + ) + assert os.path.isdir(us_dir), ( + "Fusion utility script dir does not exists. Either make sure " + "the `environments\fusion.json` is having correctly set " + "`FUSION_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" + f"FUSION_UTILITY_SCRIPTS_DIR: `{us_dir}`" + ) + + try: + __import__("avalon.fusion") + __import__("pyblish") + + except ImportError: + self.log.warning( + "pyblish: Could not load Fusion integration.", + exc_info=True + ) + + else: + # Resolve Setup integration + importlib.reload(utils) + utils.setup(self.env) diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py new file mode 100644 index 0000000000..bc747dc495 --- /dev/null +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -0,0 +1,53 @@ +import os +import platform + +from pype.lib import PreLaunchHook + + +class PhotoshopPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and execute python script of photoshop + implementation before photoshop executable. + """ + hosts = ["photoshop"] + + def execute(self): + # Pop tvpaint executable + photoshop_executable = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + python_launch_args = [ + self.python_executable(), + "-c", + ( + "^\"import avalon.photoshop;" + "avalon.photoshop.launch(\"{}\")^\"\"" + ).format(photoshop_executable) + ] + if platform.system().lower() != "windows": + new_launch_args = python_launch_args + else: + new_launch_args = [ + "cmd.exe", + "/k", + "\"{}\"".format(" ".join(python_launch_args)) + ] + + # Append as whole list as these areguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.log.warning(( + "There are unexpected launch arguments in Photoshop launch. {}" + ).format(str(remainders))) + self.launch_context.launch_args.extend(remainders) + + def python_executable(self): + """Should lead to python executable.""" + # TODO change in Pype 3 + return os.environ["PYPE_PYTHON_EXE"] diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 9f90398433..6c59a8ce20 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -22,30 +22,30 @@ class TvpaintPrelaunchHook(PreLaunchHook): hosts = ["tvpaint"] def execute(self): + # Pop tvpaint executable tvpaint_executable = self.launch_context.launch_args.pop(0) - # This should never be used! + # Pop rest of launch arguments - There should not be other arguments! remainders = [] while self.launch_context.launch_args: remainders.append(self.launch_context.launch_args.pop(0)) - self.launch_context.launch_args.append( - self.main_executable() - ) - self.launch_context.launch_args.append( - "\"{}\"".format(self.launch_script_path()) - ) - self.launch_context.launch_args.append( + new_launch_args = [ + self.main_executable(), + "\"{}\"".format(self.launch_script_path()), "\"{}\"".format(tvpaint_executable) - ) + ] # Add workfile to launch arguments workfile_path = self.workfile_path() if workfile_path: - self.launch_context.launch_args.append( + new_launch_args.append( "\"{}\"".format(workfile_path) ) + # Append as whole list as these areguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + if remainders: self.log.warning(( "There are unexpected launch arguments in TVPaint launch. {}" From 9940ff2fa9ec5dfa2435f364b5543e726999bed7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 24 Nov 2020 20:02:18 +0100 Subject: [PATCH 029/219] added maya --- pype/hooks/maya/pre_launch_args.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 pype/hooks/maya/pre_launch_args.py diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py new file mode 100644 index 0000000000..6aed54a3c3 --- /dev/null +++ b/pype/hooks/maya/pre_launch_args.py @@ -0,0 +1,17 @@ +import os +from pype.lib import PreLaunchHook + + +class MayaLaunchArguments(PreLaunchHook): + """Add path to last workfile to launch arguments.""" + order = 0 + hosts = ["maya"] + + def execute(self): + """Prepare suprocess launch arguments for NukeX.""" + if self.data.get("start_last_workfile"): + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.launch_context.launch_args.append( + "\"{}\"".format(last_workfile) + ) From 87d30338c4c0ebafd88bc9fd83a6f59b2ff74577 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 09:55:46 +0100 Subject: [PATCH 030/219] added harmony prelaunch --- pype/hooks/harmony/pre_launch_args.py | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 pype/hooks/harmony/pre_launch_args.py diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py new file mode 100644 index 0000000000..7047e67616 --- /dev/null +++ b/pype/hooks/harmony/pre_launch_args.py @@ -0,0 +1,53 @@ +import os +import platform + +from pype.lib import PreLaunchHook + + +class HarmonyPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and execute python script of harmony + implementation before harmony executable. + """ + hosts = ["harmony"] + + def execute(self): + # Pop tvpaint executable + photoshop_executable = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + python_launch_args = [ + self.python_executable(), + "-c", + ( + "^\"import avalon.harmony;" + "avalon.harmony.launch(\"{}\")^\"\"" + ).format(photoshop_executable) + ] + if platform.system().lower() != "windows": + new_launch_args = python_launch_args + else: + new_launch_args = [ + "cmd.exe", + "/k", + "\"{}\"".format(" ".join(python_launch_args)) + ] + + # Append as whole list as these areguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.log.warning(( + "There are unexpected launch arguments in Harmony launch. {}" + ).format(str(remainders))) + self.launch_context.launch_args.extend(remainders) + + def python_executable(self): + """Should lead to python executable.""" + # TODO change in Pype 3 + return os.environ["PYPE_PYTHON_EXE"] From 7174dc16ef7f3e461902bb0590cbd90ce6f56411 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 10:16:03 +0100 Subject: [PATCH 031/219] added unreal prelaunch hook --- pype/hooks/unreal/pre_workfile_preparation.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 pype/hooks/unreal/pre_workfile_preparation.py diff --git a/pype/hooks/unreal/pre_workfile_preparation.py b/pype/hooks/unreal/pre_workfile_preparation.py new file mode 100644 index 0000000000..f0e09669dc --- /dev/null +++ b/pype/hooks/unreal/pre_workfile_preparation.py @@ -0,0 +1,95 @@ +import os + +from pype.lib import ( + PreLaunchHook, + ApplicationLaunchFailed +) +from pype.hosts.unreal import lib as unreal_lib + + +class UnrealPrelaunchHook(PreLaunchHook): + """ + This hook will check if current workfile path has Unreal + project inside. IF not, it initialize it and finally it pass + path to the project by environment variable to Unreal launcher + shell script. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.signature = "( {} )".format(self.__class__.__name__) + + def execute(self): + asset_name = self.data["asset_name"] + task_name = self.data["task_name"] + workdir = self.env["AVALON_WORKDIR"] + engine_version = self.app_name.split("_")[-1] + unreal_project_name = f"{asset_name}_{task_name}" + + # Unreal is sensitive about project names longer then 20 chars + if len(unreal_project_name) > 20: + self.log.warning(( + f"Project name exceed 20 characters ({unreal_project_name})!" + )) + + # Unreal doesn't accept non alphabet characters at the start + # of the project name. This is because project name is then used + # in various places inside c++ code and there variable names cannot + # start with non-alpha. We append 'P' before project name to solve it. + # 😱 + if not unreal_project_name[:1].isalpha(): + self.log.warning(( + "Project name doesn't start with alphabet " + f"character ({unreal_project_name}). Appending 'P'" + )) + unreal_project_name = f"P{unreal_project_name}" + + project_path = os.path.join(workdir, unreal_project_name) + + self.log.info(( + f"{self.signature} requested UE4 version: " + f"[ {engine_version} ]" + )) + + detected = unreal_lib.get_engine_versions() + detected_str = ', '.join(detected.keys()) or 'none' + self.log.info(( + f"{self.signature} detected UE4 versions: " + f"[ {detected_str} ]" + )) + + engine_version = ".".join(engine_version.split(".")[:2]) + if engine_version not in detected.keys(): + raise ApplicationLaunchFailed(( + f"{self.signature} requested version not " + f"detected [ {engine_version} ]" + )) + + os.makedirs(project_path, exist_ok=True) + + project_file = os.path.join( + project_path, + f"{unreal_project_name}.uproject" + ) + if not os.path.isfile(project_file): + engine_path = detected[engine_version] + self.log.info(( + f"{self.signature} creating unreal " + f"project [ {unreal_project_name} ]" + )) + # Set "AVALON_UNREAL_PLUGIN" to current process environment for + # execution of `create_unreal_project` + env_key = "AVALON_UNREAL_PLUGIN" + if self.env.get(env_key): + os.environ[env_key] = self.env[env_key] + + unreal_lib.create_unreal_project( + unreal_project_name, + engine_version, + project_path, + engine_path=engine_path + ) + + # Append project file to launch arguments + self.launch_context.launch_args.append(f"\"{project_file}\"") From cc70fd75524a7fe2a34c8f6f6b0f9a50c28994d4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 10:50:03 +0100 Subject: [PATCH 032/219] ftrack sync can use application manager for applications sync --- pype/modules/ftrack/lib/avalon_sync.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 7ff5283d6a..97116317af 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -17,7 +17,10 @@ from bson.errors import InvalidId from pymongo import UpdateOne import ftrack_api from pype.api import config - +from pype.lib import ( + ApplicationManager, + env_value_to_bool +) log = Logger().get_logger(__name__) @@ -186,12 +189,28 @@ def get_project_apps(in_app_list): dictionary of warnings """ apps = [] + warnings = collections.defaultdict(list) + + if env_value_to_bool("PYPE_USE_APP_MANAGER", default=False): + missing_app_msg = "Missing definition of application" + application_manager = ApplicationManager() + for app_name in in_app_list: + app = application_manager.applications.get(app_name) + if app: + apps.append({ + "name": app_name, + "label": app.full_label + }) + else: + warnings[missing_app_msg].append(app_name) + return apps, warnings + # TODO report missing_toml_msg = "Missing config file for application" error_msg = ( "Unexpected error happend during preparation of application" ) - warnings = collections.defaultdict(list) + for app in in_app_list: try: toml_path = avalon.lib.which_app(app) From 0de4a563eeef70bbc7b712f8872cbb491cb3eb9b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 10:50:26 +0100 Subject: [PATCH 033/219] added TVPaint to applications --- .../system_settings/global/applications.json | 49 +++++++++++++++++++ .../host_settings/schema_tvpaint.json | 41 ++++++++++++++++ .../system_schema/schema_applications.json | 4 ++ 3 files changed, 94 insertions(+) create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_tvpaint.json diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index 4bcea2fa30..d6ab2d7829 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -855,6 +855,55 @@ } } }, + "tvpaint": { + "enabled": true, + "label": "TVPaint", + "icon": "{}/app_icons/tvpaint.png", + "is_host": true, + "environment": { + "__environment_keys__": { + "tvpaint": [] + } + }, + "variants": { + "tvpaint_Animation 11 (64bits)": { + "enabled": true, + "label": "", + "variant_label": "Animation 11 (64bits)", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 (64bits)\\TVPaint Animation 11 (64bits).exe" + ], + "darwin": [], + "linux": [] + }, + "environment": { + "__environment_keys__": { + "tvpaint_Animation 11 (64bits)": [] + } + } + }, + "tvpaint_Animation 11 (32bits)": { + "enabled": true, + "label": "", + "variant_label": "Animation 11 (32bits)", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files (x86)\\TVPaint Developpement\\TVPaint Animation 11 (32bits)\\TVPaint Animation 11 (32bits).exe" + ], + "darwin": [], + "linux": [] + }, + "environment": { + "__environment_keys__": { + "tvpaint_Animation 11 (32bits)": [] + } + } + } + } + }, "photoshop": { "enabled": true, "label": "Adobe Photoshop", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_tvpaint.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_tvpaint.json new file mode 100644 index 0000000000..09e5b1d907 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_tvpaint.json @@ -0,0 +1,41 @@ +{ + "type": "dict", + "key": "tvpaint", + "label": "TVPaint", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "schema_template", + "name": "template_host_unchangables" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json", + "env_group_key": "tvpaint" + }, + { + "type": "dict-invisible", + "key": "variants", + "children": [{ + "type": "schema_template", + "name": "template_host_variant", + "template_data": [ + { + "host_version": "Animation 11 (64bits)", + "host_name": "tvpaint" + }, + { + "host_version": "Animation 11 (32bits)", + "host_name": "tvpaint" + } + ] + }] + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json index ebfa4482bb..65cd16049e 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json @@ -65,6 +65,10 @@ "type": "schema", "name": "schema_harmony" }, + { + "type": "schema", + "name": "schema_tvpaint" + }, { "type": "schema", "name": "schema_photoshop" From 9ff8366ca44f9d05a52b53af802ed28f0eb00a35 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 11:33:38 +0100 Subject: [PATCH 034/219] use different loading for python 3 --- pype/lib/python_module_tools.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pype/lib/python_module_tools.py b/pype/lib/python_module_tools.py index 61a3b1b09e..2ce2f60dca 100644 --- a/pype/lib/python_module_tools.py +++ b/pype/lib/python_module_tools.py @@ -1,9 +1,12 @@ import os +import sys import types +import importlib import inspect import logging log = logging.getLogger(__name__) +PY3 = sys.version_info[0] == 3 def modules_from_path(folder_path): @@ -39,11 +42,20 @@ def modules_from_path(folder_path): try: # Prepare module object where content of file will be parsed module = types.ModuleType(mod_name) - module.__file__ = full_path - with open(full_path) as _stream: - # Execute content and store it to module object - exec(_stream.read(), module.__dict__) + if PY3: + # Use loader so module has full specs + module_loader = importlib.machinery.SourceFileLoader( + mod_name, full_path + ) + module_loader.exec_module(module) + else: + # Execute module code and store content to module + with open(full_path) as _stream: + # Execute content and store it to module object + exec(_stream.read(), module.__dict__) + + module.__file__ = full_path modules.append(module) From 80a12c808e42de3ba281c1de83739734b13e6202 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 11:40:31 +0100 Subject: [PATCH 035/219] fix key error --- pype/hooks/celaction/pre_celaction_registers.py | 2 +- pype/hooks/tvpaint/pre_launch_args.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/hooks/celaction/pre_celaction_registers.py b/pype/hooks/celaction/pre_celaction_registers.py index c638ce3c0b..b65deda7f5 100644 --- a/pype/hooks/celaction/pre_celaction_registers.py +++ b/pype/hooks/celaction/pre_celaction_registers.py @@ -91,7 +91,7 @@ class CelactionPrelaunchHook(PreLaunchHook): winreg.SetValueEx(hKey, "Valid", 0, winreg.REG_DWORD, 1) def workfile_path(self): - workfile_path = self.data["last_workfile"] + workfile_path = self.data["last_workfile_path"] # copy workfile from template if doesnt exist any on path if not os.path.exists(workfile_path): diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 6c59a8ce20..1901041d94 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -67,7 +67,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): return script_path def workfile_path(self): - workfile_path = self.data["last_workfile"] + workfile_path = self.data["last_workfile_path"] # copy workfile from template if doesnt exist any on path if not os.path.exists(workfile_path): From a40279d4567d0b2fff72e7b409a37902083d5154 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 11:50:45 +0100 Subject: [PATCH 036/219] fix minor clean arguments bug --- pype/lib/applications.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 9d34a8b22c..c1c6fc9301 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -1041,7 +1041,9 @@ class ApplicationLaunchContext: # Prepare subprocess args args = self.clear_launch_args(self.launch_args) self.log.debug( - "Launching \"{}\" with args: {}".format(self.app_name, args) + "Launching \"{}\" with args ({}): {}".format( + self.app_name, len(args), args + ) ) # Run process self.process = subprocess.Popen(args, **self.kwargs) @@ -1095,7 +1097,7 @@ class ApplicationLaunchContext: for _arg in arg: new_args.append(_arg) else: - new_args.append(args) + new_args.append(arg) args = new_args if all_cleared: From 92d34a0a66bc778f98e12192970f0c9dc92197e5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 13:18:51 +0100 Subject: [PATCH 037/219] fixed tvpaint launch --- pype/hooks/tvpaint/pre_launch_args.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 1901041d94..60ddcb6167 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -32,7 +32,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): new_launch_args = [ self.main_executable(), - "\"{}\"".format(self.launch_script_path()), + self.launch_script_path(), "\"{}\"".format(tvpaint_executable) ] @@ -43,6 +43,15 @@ class TvpaintPrelaunchHook(PreLaunchHook): "\"{}\"".format(workfile_path) ) + # How to create new command line + # if platform.system().lower() == "windows": + # new_launch_args = [ + # "cmd.exe", + # "/c", + # "Call cmd.exe /k", + # *new_launch_args + # ] + # Append as whole list as these areguments should not be separated self.launch_context.launch_args.append(new_launch_args) @@ -55,7 +64,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): def main_executable(self): """Should lead to python executable.""" # TODO change in Pype 3 - return os.environ["PYPE_PYTHON_EXE"] + return os.path.normpath(os.environ["PYPE_PYTHON_EXE"]) def launch_script_path(self): avalon_dir = os.path.dirname(os.path.abspath(avalon.__file__)) From 100dec2c7a9e4295caf980322825845f7b0e9ee7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 25 Nov 2020 13:24:51 +0100 Subject: [PATCH 038/219] feat(imageio): adding default schema --- .../defaults/project_anatomy/{colorspace.json => imageio.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/settings/defaults/project_anatomy/{colorspace.json => imageio.json} (100%) diff --git a/pype/settings/defaults/project_anatomy/colorspace.json b/pype/settings/defaults/project_anatomy/imageio.json similarity index 100% rename from pype/settings/defaults/project_anatomy/colorspace.json rename to pype/settings/defaults/project_anatomy/imageio.json From 7df18a6b417c2897bb7d651a1d33c1b51f8e349e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 13:44:24 +0100 Subject: [PATCH 039/219] added imageio key to anatomy --- .../projects_schema/0_project_gui_schema.json | 3 ++ .../schema_anatomy_imageio.json | 8 +++++ .../settings/widgets/anatomy_types.py | 33 +++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index cf95bf4c45..6f69340560 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -14,6 +14,9 @@ "type": "anatomy_templates", "key": "templates", "is_file": true + }, { + "type": "schema", + "name": "schema_anatomy_imageio" } ] }, { diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json new file mode 100644 index 0000000000..3fdbd3f7f9 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json @@ -0,0 +1,8 @@ +{ + "type": "dict", + "key": "imageio", + "label": "< ENTER LABEL HERE >", + "is_file": true, + "children": [ + ] +} diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index e1a726187c..ef89a802bc 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -1,7 +1,11 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget from .item_types import ( - SettingObject, ModifiableDict, PathWidget, RawJsonWidget + SettingObject, + ModifiableDict, + PathWidget, + RawJsonWidget, + DictWidget ) from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET, METADATA_KEY @@ -50,14 +54,18 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): children_data = input_data["children"] roots_input_data = {} templates_input_data = {} + imageio_input_data = {} for child in children_data: if child["type"] == "anatomy_roots": roots_input_data = child elif child["type"] == "anatomy_templates": templates_input_data = child + elif child["key"] == "imageio": + imageio_input_data = child self.root_widget = RootsWidget(roots_input_data, self) self.templates_widget = TemplatesWidget(templates_input_data, self) + self.imageio_widget = DictWidget(imageio_input_data, self) self.setAttribute(QtCore.Qt.WA_StyledBackground) @@ -75,6 +83,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): content_layout.addWidget(self.root_widget) content_layout.addWidget(self.templates_widget) + content_layout.addWidget(self.imageio_widget) body_widget.set_content_widget(content_widget) @@ -83,6 +92,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget.value_changed.connect(self._on_value_change) self.templates_widget.value_changed.connect(self._on_value_change) + self.imageio_widget.value_changed.connect(self._on_value_change) def update_default_values(self, parent_values): self._state = None @@ -95,6 +105,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget.update_default_values(value) self.templates_widget.update_default_values(value) + self.imageio_widget.update_default_values(value) def update_studio_values(self, parent_values): self._state = None @@ -107,6 +118,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget.update_studio_values(value) self.templates_widget.update_studio_values(value) + self.imageio_widget.update_studio_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -119,6 +131,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget.apply_overrides(value) self.templates_widget.apply_overrides(value) + self.imageio_widget.apply_overrides(value) def set_value(self, value): raise TypeError("AnatomyWidget does not allow to use `set_value`") @@ -154,6 +167,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): def hierarchical_style_update(self): self.root_widget.hierarchical_style_update() self.templates_widget.hierarchical_style_update() + self.imageio_widget.hierarchical_style_update() self.update_style() @property @@ -161,6 +175,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): return ( self.root_widget.child_has_studio_override or self.templates_widget.child_has_studio_override + or self.imageio_widget.child_has_studio_override ) @property @@ -168,6 +183,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): return ( self.root_widget.child_modified or self.templates_widget.child_modified + or self.imageio_widget.child_modified ) @property @@ -175,6 +191,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): return ( self.root_widget.child_overriden or self.templates_widget.child_overriden + or self.imageio_widget.child_overriden ) @property @@ -182,27 +199,33 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): return ( self.root_widget.child_invalid or self.templates_widget.child_invalid + or self.imageio_widget.child_invalid ) def set_as_overriden(self): self.root_widget.set_as_overriden() self.templates_widget.set_as_overriden() + self.imageio_widget.set_as_overriden() def remove_overrides(self): self.root_widget.remove_overrides() self.templates_widget.remove_overrides() + self.imageio_widget.remove_overrides() def reset_to_pype_default(self): self.root_widget.reset_to_pype_default() self.templates_widget.reset_to_pype_default() + self.imageio_widget.reset_to_pype_default() def set_studio_default(self): self.root_widget.set_studio_default() self.templates_widget.set_studio_default() + self.imageio_widget.set_studio_default() def discard_changes(self): self.root_widget.discard_changes() self.templates_widget.discard_changes() + self.imageio_widget.discard_changes() def overrides(self): if self.child_overriden: @@ -213,14 +236,20 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): output = {} output.update(self.root_widget.config_value()) output.update(self.templates_widget.config_value()) + output.update(self.imageio_widget.config_value()) return output def studio_overrides(self): if ( self.root_widget.child_has_studio_override or self.templates_widget.child_has_studio_override + or self.imageio_widget.child_has_studio_override ): - groups = [self.root_widget.key, self.templates_widget.key] + groups = [ + self.root_widget.key, + self.templates_widget.key, + self.imageio_widget.key + ] value = self.config_value() value[self.key][METADATA_KEY] = {"groups": groups} return value, True From 674f287fe17f476991dcfff4fd718385366314d0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 13:53:08 +0100 Subject: [PATCH 040/219] change harmony and photoshop args --- pype/hooks/harmony/pre_launch_args.py | 16 +++++++--------- pype/hooks/photoshop/pre_launch_args.py | 17 ++++++++--------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py index 7047e67616..eabe1e6f87 100644 --- a/pype/hooks/harmony/pre_launch_args.py +++ b/pype/hooks/harmony/pre_launch_args.py @@ -21,7 +21,7 @@ class HarmonyPrelaunchHook(PreLaunchHook): while self.launch_context.launch_args: remainders.append(self.launch_context.launch_args.pop(0)) - python_launch_args = [ + new_launch_args = [ self.python_executable(), "-c", ( @@ -29,14 +29,12 @@ class HarmonyPrelaunchHook(PreLaunchHook): "avalon.harmony.launch(\"{}\")^\"\"" ).format(photoshop_executable) ] - if platform.system().lower() != "windows": - new_launch_args = python_launch_args - else: - new_launch_args = [ - "cmd.exe", - "/k", - "\"{}\"".format(" ".join(python_launch_args)) - ] + # if platform.system().lower() == "windows": + # new_launch_args = [ + # "cmd.exe", + # "/k", + # "\"{}\"".format(" ".join(new_launch_args)) + # ] # Append as whole list as these areguments should not be separated self.launch_context.launch_args.append(new_launch_args) diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py index bc747dc495..77449a8269 100644 --- a/pype/hooks/photoshop/pre_launch_args.py +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -21,7 +21,7 @@ class PhotoshopPrelaunchHook(PreLaunchHook): while self.launch_context.launch_args: remainders.append(self.launch_context.launch_args.pop(0)) - python_launch_args = [ + new_launch_args = [ self.python_executable(), "-c", ( @@ -29,14 +29,13 @@ class PhotoshopPrelaunchHook(PreLaunchHook): "avalon.photoshop.launch(\"{}\")^\"\"" ).format(photoshop_executable) ] - if platform.system().lower() != "windows": - new_launch_args = python_launch_args - else: - new_launch_args = [ - "cmd.exe", - "/k", - "\"{}\"".format(" ".join(python_launch_args)) - ] + + # if platform.system().lower() == "windows": + # new_launch_args = [ + # "cmd.exe", + # "/k", + # "\"{}\"".format(" ".join(new_launch_args)) + # ] # Append as whole list as these areguments should not be separated self.launch_context.launch_args.append(new_launch_args) From 75e1cadc31b5525a2583f1e71d9339066ec75006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 25 Nov 2020 14:02:06 +0100 Subject: [PATCH 041/219] help 4.26 unreal to create projects --- pype/hosts/unreal/lib.py | 19 +++++++++++-------- pype/lib/hooks.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pype/hosts/unreal/lib.py b/pype/hosts/unreal/lib.py index 5534c0cbc8..d6bfba436e 100644 --- a/pype/hosts/unreal/lib.py +++ b/pype/hosts/unreal/lib.py @@ -246,15 +246,18 @@ def create_unreal_project(project_name: str, with open(project_file, mode="w") as pf: json.dump(data, pf, indent=4) - # ensure we have PySide installed in engine - # TODO: make it work for other platforms 🍎 🐧 - if platform.system().lower() == "windows": - python_path = os.path.join(engine_path, "Engine", "Binaries", - "ThirdParty", "Python", "Win64", - "python.exe") + # UE < 4.26 have Python2 by default, so we need PySide + # but we will not need it in 4.26 and up + if int(ue_version.split(".")[1]) < 26: + # ensure we have PySide installed in engine + # TODO: make it work for other platforms 🍎 🐧 + if platform.system().lower() == "windows": + python_path = os.path.join(engine_path, "Engine", "Binaries", + "ThirdParty", "Python", "Win64", + "python.exe") - subprocess.run([python_path, "-m", - "pip", "install", "pyside"]) + subprocess.run([python_path, "-m", + "pip", "install", "pyside"]) if dev_mode or preset["dev_mode"]: _prepare_cpp_project(project_file, engine_path) diff --git a/pype/lib/hooks.py b/pype/lib/hooks.py index 425ad36342..bb5406572e 100644 --- a/pype/lib/hooks.py +++ b/pype/lib/hooks.py @@ -55,7 +55,7 @@ def execute_hook(hook, *args, **kwargs): module.__file__ = abspath try: - with open(abspath) as f: + with open(abspath, errors='ignore') as f: six.exec_(f.read(), module.__dict__) sys.modules[abspath] = module From da1a6ab320b687210c347048a6d1d45e9eb2b812 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 14:24:32 +0100 Subject: [PATCH 042/219] changed env groups in example to not match system env groups --- .../gui_schemas/system_schema/example_schema.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index 7612e54116..f09ad86a65 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -13,16 +13,16 @@ "type": "schema_template", "name": "example_template", "template_data": { - "host_label": "Maya 2019", - "host_name": "maya_2019", + "host_label": "Application 1", + "host_name": "app_1", "multipath_executables": false } }, { "type": "schema_template", "name": "example_template", "template_data": { - "host_label": "Maya 2020", - "host_name": "maya_2020" + "host_label": "Application 2", + "host_name": "app_2" } } ] From fc248c5eebe350924cd4d0ce29e14248dfb3e975 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 25 Nov 2020 14:27:27 +0100 Subject: [PATCH 043/219] test example schemas --- .../settings/gui_schemas/system_schema/schema_main.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json index 8e8798149c..3ec652c302 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json @@ -17,6 +17,9 @@ }, { "type": "schema", "name": "schema_tools" + }, { + "type": "schema", + "name": "example_schema" }] } ] From 4c990a7527d14e6f2dfdb4eae3b95a3189060496 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 14:42:04 +0100 Subject: [PATCH 044/219] fixed validation of ListStrictWidget --- pype/tools/settings/settings/widgets/base.py | 2 +- pype/tools/settings/settings/widgets/item_types.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 3f842602ca..692de617df 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -795,7 +795,7 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = default_values = lib.convert_data_to_gui_data( + default_values = lib.convert_data_to_gui_data( {"project": default_settings()} ) for input_field in self.input_fields: diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index fd5364ea17..10fd757d3e 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1897,8 +1897,6 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): return self._default_input_value def set_value(self, value): - self.validate_value(value) - if self._is_overriden: method_name = "apply_overrides" elif not self._has_studio_override: From e1c9cd637bd92b867394fc1a031c138e4680b00c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 14:55:27 +0100 Subject: [PATCH 045/219] fix launch args --- pype/hooks/tvpaint/pre_launch_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 60ddcb6167..7a5acb08fa 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -33,7 +33,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): new_launch_args = [ self.main_executable(), self.launch_script_path(), - "\"{}\"".format(tvpaint_executable) + tvpaint_executable ] # Add workfile to launch arguments From 7e68d5b6498daa93c92025244fc6a7d80381bb39 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 15:49:23 +0100 Subject: [PATCH 046/219] hound cleanups --- pype/hooks/harmony/pre_launch_args.py | 1 - pype/hooks/photoshop/pre_launch_args.py | 1 - pype/hooks/tvpaint/pre_install_pywin.py | 3 ++- pype/hooks/tvpaint/pre_launch_args.py | 7 ++----- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py index eabe1e6f87..cafb2a4c60 100644 --- a/pype/hooks/harmony/pre_launch_args.py +++ b/pype/hooks/harmony/pre_launch_args.py @@ -1,5 +1,4 @@ import os -import platform from pype.lib import PreLaunchHook diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py index 77449a8269..5b326f882b 100644 --- a/pype/hooks/photoshop/pre_launch_args.py +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -1,5 +1,4 @@ import os -import platform from pype.lib import PreLaunchHook diff --git a/pype/hooks/tvpaint/pre_install_pywin.py b/pype/hooks/tvpaint/pre_install_pywin.py index 20d4b2aae7..b51267934b 100644 --- a/pype/hooks/tvpaint/pre_install_pywin.py +++ b/pype/hooks/tvpaint/pre_install_pywin.py @@ -26,8 +26,9 @@ class PreInstallPyWin(PreLaunchHook): try: output = _subprocess( - ["pip", "install", "pywin32==227"], logger=self.log + ["pip", "install", "pywin32==227"] ) + self.log.debug("Pip install pywin32 output:\n{}'".format(output)) except RuntimeError: msg = "Installation of python module `pywin32` crashed." self.log.warning(msg, exc_info=True) diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 7a5acb08fa..9d80e6fddb 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -2,11 +2,8 @@ import os import shutil from pype.hosts import tvpaint -from pype.lib import ( - PreLaunchHook, - ApplicationLaunchFailed, - _subprocess -) +from pype.lib import PreLaunchHook + import avalon From c77946c181838c8dfa99b932ecd256f86967ad08 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 15:52:15 +0100 Subject: [PATCH 047/219] added aftereffects prelaunch hook --- pype/hooks/aftereffects/pre_launch_args.py | 45 ++++++++++++++++++++++ pype/hooks/harmony/pre_launch_args.py | 6 --- pype/hooks/photoshop/pre_launch_args.py | 7 ---- 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 pype/hooks/aftereffects/pre_launch_args.py diff --git a/pype/hooks/aftereffects/pre_launch_args.py b/pype/hooks/aftereffects/pre_launch_args.py new file mode 100644 index 0000000000..9bd3a41ee4 --- /dev/null +++ b/pype/hooks/aftereffects/pre_launch_args.py @@ -0,0 +1,45 @@ +import os + +from pype.lib import PreLaunchHook + + +class AfterEffectsPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and execute python script of AfterEffects + implementation before AfterEffects executable. + """ + hosts = ["aftereffects"] + + def execute(self): + # Pop tvpaint executable + photoshop_executable = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + new_launch_args = [ + self.python_executable(), + "-c", + ( + "^\"import avalon.aftereffects;" + "avalon.aftereffects.launch(\"{}\")^\"\"" + ).format(photoshop_executable) + ] + + # Append as whole list as these areguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.log.warning(( + "There are unexpected launch arguments " + "in AfterEffects launch. {}" + ).format(str(remainders))) + self.launch_context.launch_args.extend(remainders) + + def python_executable(self): + """Should lead to python executable.""" + # TODO change in Pype 3 + return os.environ["PYPE_PYTHON_EXE"] diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py index cafb2a4c60..48a72d6ec9 100644 --- a/pype/hooks/harmony/pre_launch_args.py +++ b/pype/hooks/harmony/pre_launch_args.py @@ -28,12 +28,6 @@ class HarmonyPrelaunchHook(PreLaunchHook): "avalon.harmony.launch(\"{}\")^\"\"" ).format(photoshop_executable) ] - # if platform.system().lower() == "windows": - # new_launch_args = [ - # "cmd.exe", - # "/k", - # "\"{}\"".format(" ".join(new_launch_args)) - # ] # Append as whole list as these areguments should not be separated self.launch_context.launch_args.append(new_launch_args) diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py index 5b326f882b..840e4e2a32 100644 --- a/pype/hooks/photoshop/pre_launch_args.py +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -29,13 +29,6 @@ class PhotoshopPrelaunchHook(PreLaunchHook): ).format(photoshop_executable) ] - # if platform.system().lower() == "windows": - # new_launch_args = [ - # "cmd.exe", - # "/k", - # "\"{}\"".format(" ".join(new_launch_args)) - # ] - # Append as whole list as these areguments should not be separated self.launch_context.launch_args.append(new_launch_args) From 576c359b4ff8de3445babbcf46c1f6817580268d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 15:56:55 +0100 Subject: [PATCH 048/219] added aftereffects to application schemas --- .../system_settings/global/applications.json | 67 ++++++++++++++++++- .../host_settings/schema_aftereffects.json | 41 ++++++++++++ .../system_schema/schema_applications.json | 4 ++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_aftereffects.json diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index d6ab2d7829..3eda5e036d 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -862,8 +862,11 @@ "is_host": true, "environment": { "__environment_keys__": { - "tvpaint": [] - } + "tvpaint": [ + "PYPE_LOG_NO_COLORS" + ] + }, + "PYPE_LOG_NO_COLORS": "True" }, "variants": { "tvpaint_Animation 11 (64bits)": { @@ -944,6 +947,66 @@ } } }, + "aftereffects": { + "enabled": true, + "label": "Adobe AfterEffects", + "icon": "{}/app_icons/aftereffects.png", + "is_host": true, + "environment": { + "__environment_keys__": { + "aftereffects": [ + "AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH", + "PYTHONPATH", + "PYPE_LOG_NO_COLORS", + "WEBSOCKET_URL", + "WORKFILES_SAVE_AS" + ] + }, + "AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH": "1", + "PYTHONPATH": "{PYTHONPATH}", + "PYPE_LOG_NO_COLORS": "Yes", + "WEBSOCKET_URL": "ws://localhost:8097/ws/", + "WORKFILES_SAVE_AS": "Yes" + }, + "variants": { + "aftereffects_2020": { + "enabled": true, + "label": "", + "variant_label": "2020", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files\\Adobe\\Adobe After Effects 2020\\Support Files\\AfterFX.exe" + ], + "darwin": [], + "linux": [] + }, + "environment": { + "__environment_keys__": { + "aftereffects_2020": [] + } + } + }, + "aftereffects_2021": { + "enabled": true, + "label": "", + "variant_label": "2021", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files\\Adobe\\Adobe After Effects 2021\\Support Files\\AfterFX.exe" + ], + "darwin": [], + "linux": [] + }, + "environment": { + "__environment_keys__": { + "aftereffects_2021": [] + } + } + } + } + }, "celaction": { "enabled": true, "label": "CelAction 2D", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_aftereffects.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_aftereffects.json new file mode 100644 index 0000000000..073d57b870 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_aftereffects.json @@ -0,0 +1,41 @@ +{ + "type": "dict", + "key": "aftereffects", + "label": "Adobe AfterEffects", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "schema_template", + "name": "template_host_unchangables" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json", + "env_group_key": "aftereffects" + }, + { + "type": "dict-invisible", + "key": "variants", + "children": [{ + "type": "schema_template", + "name": "template_host_variant", + "template_data": [ + { + "host_version": "2020", + "host_name": "aftereffects" + }, + { + "host_version": "2021", + "host_name": "aftereffects" + } + ] + }] + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json index 65cd16049e..1c983bcff2 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_applications.json @@ -73,6 +73,10 @@ "type": "schema", "name": "schema_photoshop" }, + { + "type": "schema", + "name": "schema_aftereffects" + }, { "type": "schema", "name": "schema_celaction" From 5159d3dabdec93aaa3291578fd68f562f89ae000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 25 Nov 2020 15:58:58 +0100 Subject: [PATCH 049/219] Maya: Vray handling of default aov --- pype/hosts/maya/expected_files.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index 8d225bc13d..05b0785251 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -561,24 +561,6 @@ class ExpectedFilesVray(AExpectedFiles): layer_data["padding"] = cmds.getAttr("vraySettings.fileNamePadding") return layer_data - def get_files(self): - """Get expected files. - - This overrides :func:`AExpectedFiles.get_files()` as we - we need to add one sequence for plain beauty if AOVs are enabled - as vray output beauty without 'beauty' in filename. - - """ - expected_files = super(ExpectedFilesVray, self).get_files() - - layer_data = self._get_layer_data() - if layer_data.get("enabledAOVs"): - expected_files[0][u"beauty"] = self._generate_single_file_sequence( - layer_data - ) # noqa: E501 - - return expected_files - def get_aovs(self): """Get all AOVs. @@ -630,6 +612,9 @@ class ExpectedFilesVray(AExpectedFiles): # todo: find how vray set format for AOVs enabled_aovs.append( (self._get_vray_aov_name(aov), default_ext)) + enabled_aovs.append( + (u"beauty", default_ext) + ) return enabled_aovs def _get_vray_aov_name(self, node): From 8f08412ec7f2129298b0409df417e27ba4b46e9d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 17:04:07 +0100 Subject: [PATCH 050/219] fixed python launch --- pype/hooks/aftereffects/pre_launch_args.py | 8 ++++---- pype/hooks/harmony/pre_launch_args.py | 8 ++++---- pype/hooks/photoshop/pre_launch_args.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/hooks/aftereffects/pre_launch_args.py b/pype/hooks/aftereffects/pre_launch_args.py index 9bd3a41ee4..01406d495c 100644 --- a/pype/hooks/aftereffects/pre_launch_args.py +++ b/pype/hooks/aftereffects/pre_launch_args.py @@ -13,7 +13,7 @@ class AfterEffectsPrelaunchHook(PreLaunchHook): def execute(self): # Pop tvpaint executable - photoshop_executable = self.launch_context.launch_args.pop(0) + aftereffects_executable = self.launch_context.launch_args.pop(0) # Pop rest of launch arguments - There should not be other arguments! remainders = [] @@ -24,9 +24,9 @@ class AfterEffectsPrelaunchHook(PreLaunchHook): self.python_executable(), "-c", ( - "^\"import avalon.aftereffects;" - "avalon.aftereffects.launch(\"{}\")^\"\"" - ).format(photoshop_executable) + "import avalon.aftereffects;" + "avalon.aftereffects.launch(\"{}\")" + ).format(aftereffects_executable) ] # Append as whole list as these areguments should not be separated diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py index 48a72d6ec9..70fac5bb76 100644 --- a/pype/hooks/harmony/pre_launch_args.py +++ b/pype/hooks/harmony/pre_launch_args.py @@ -13,7 +13,7 @@ class HarmonyPrelaunchHook(PreLaunchHook): def execute(self): # Pop tvpaint executable - photoshop_executable = self.launch_context.launch_args.pop(0) + harmony_executable = self.launch_context.launch_args.pop(0) # Pop rest of launch arguments - There should not be other arguments! remainders = [] @@ -24,9 +24,9 @@ class HarmonyPrelaunchHook(PreLaunchHook): self.python_executable(), "-c", ( - "^\"import avalon.harmony;" - "avalon.harmony.launch(\"{}\")^\"\"" - ).format(photoshop_executable) + "import avalon.harmony;" + "avalon.harmony.launch(\"{}\")" + ).format(harmony_executable) ] # Append as whole list as these areguments should not be separated diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py index 840e4e2a32..2c88f62157 100644 --- a/pype/hooks/photoshop/pre_launch_args.py +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -24,8 +24,8 @@ class PhotoshopPrelaunchHook(PreLaunchHook): self.python_executable(), "-c", ( - "^\"import avalon.photoshop;" - "avalon.photoshop.launch(\"{}\")^\"\"" + "import avalon.photoshop;" + "avalon.photoshop.launch(\"{}\")" ).format(photoshop_executable) ] From ec0127c79a17fcf51a87c0d4658d744084ed3f01 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 17:06:10 +0100 Subject: [PATCH 051/219] added photoshop 2021 --- .../system_settings/global/applications.json | 22 ++++++++++++++++++- .../host_settings/schema_photoshop.json | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index 3eda5e036d..c638f1e3d2 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -935,7 +935,9 @@ "variant_label": "2020", "icon": "", "executables": { - "windows": [], + "windows": [ + "C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe" + ], "darwin": [], "linux": [] }, @@ -944,6 +946,24 @@ "photoshop_2020": [] } } + }, + "photoshop_2021": { + "enabled": true, + "label": "", + "variant_label": "2021", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe" + ], + "darwin": [], + "linux": [] + }, + "environment": { + "__environment_keys__": { + "photoshop_2021": [] + } + } } } }, diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_photoshop.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_photoshop.json index f86f6ff055..0c27611b29 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_photoshop.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_photoshop.json @@ -29,6 +29,10 @@ { "host_version": "2020", "host_name": "photoshop" + }, + { + "host_version": "2021", + "host_name": "photoshop" } ] }] From 3b6eec2b5b6df5b88abb0398a14b7ab4e034020c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 25 Nov 2020 17:55:36 +0100 Subject: [PATCH 052/219] remove `_beauty` from vray output files --- pype/hosts/maya/expected_files.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index 05b0785251..bd58b10d74 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -561,6 +561,30 @@ class ExpectedFilesVray(AExpectedFiles): layer_data["padding"] = cmds.getAttr("vraySettings.fileNamePadding") return layer_data + def get_files(self): + """Get expected files. + + This overrides :func:`AExpectedFiles.get_files()` as we + we need to add one sequence for plain beauty if AOVs are enabled + as vray output beauty without 'beauty' in filename. + + """ + expected_files = super(ExpectedFilesVray, self).get_files() + + layer_data = self._get_layer_data() + # remove 'beauty' from filenames as vray doesn't output it + update = {} + if layer_data.get("enabledAOVs"): + for aov, seq in expected_files[0].items(): + if aov.startswith("beauty"): + new_list = [] + for f in seq: + new_list.append(f.replace("_beauty", "")) + update[aov] = new_list + + expected_files[0].update(update) + return expected_files + def get_aovs(self): """Get all AOVs. From b3beb2baf120a9316f3fcfb9d6606a39bae54314 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 25 Nov 2020 18:22:13 +0100 Subject: [PATCH 053/219] start on ftrack settings --- .../ftrack/ftrack_custom_attributes.json | 56 --- .../ftrack/partnership_ftrack_cred.json | 5 - .../system_settings/global/modules.json | 12 +- .../projects_schema/0_project_gui_schema.json | 3 +- .../projects_schema/1_plugins_gui_schema.json | 326 ------------- .../schemas/schema_project_celaction.json | 50 ++ .../schemas/schema_project_ftrack.json | 129 ++++++ .../schemas/schema_project_hiero.json | 43 ++ .../schemas/schema_project_maya.json | 28 ++ .../schemas/schema_project_nuke.json | 140 ++++++ .../schemas/schema_project_resolve.json | 35 ++ .../module_settings/schema_ftrack.json | 147 ++++++ .../system_schema/schema_modules.json | 430 +++++++----------- 13 files changed, 745 insertions(+), 659 deletions(-) delete mode 100644 pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json diff --git a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json index f03d473cd0..371be3b8d8 100644 --- a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json +++ b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json @@ -10,47 +10,6 @@ "config": { "isdecimal": true } -}, { - "label": "Applications", - "key": "applications", - "type": "enumerator", - "entity_type": "show", - "group": "avalon", - "config": { - "multiselect": true, - "data": [ - {"blender_2.80": "Blender 2.80"}, - {"blender_2.81": "Blender 2.81"}, - {"blender_2.82": "Blender 2.82"}, - {"blender_2.83": "Blender 2.83"}, - {"celaction_local": "CelAction2D Local"}, - {"maya_2017": "Maya 2017"}, - {"maya_2018": "Maya 2018"}, - {"maya_2019": "Maya 2019"}, - {"nuke_10.0": "Nuke 10.0"}, - {"nuke_11.2": "Nuke 11.2"}, - {"nuke_11.3": "Nuke 11.3"}, - {"nuke_12.0": "Nuke 12.0"}, - {"nukex_10.0": "NukeX 10.0"}, - {"nukex_11.2": "NukeX 11.2"}, - {"nukex_11.3": "NukeX 11.3"}, - {"nukex_12.0": "NukeX 12.0"}, - {"nukestudio_10.0": "NukeStudio 10.0"}, - {"nukestudio_11.2": "NukeStudio 11.2"}, - {"nukestudio_11.3": "NukeStudio 11.3"}, - {"nukestudio_12.0": "NukeStudio 12.0"}, - {"harmony_17": "Harmony 17"}, - {"houdini_16.5": "Houdini 16.5"}, - {"houdini_17": "Houdini 17"}, - {"houdini_18": "Houdini 18"}, - {"photoshop_2020": "Photoshop 2020"}, - {"python_3": "Python 3"}, - {"python_2": "Python 2"}, - {"premiere_2019": "Premiere Pro 2019"}, - {"premiere_2020": "Premiere Pro 2020"}, - {"resolve_16": "BM DaVinci Resolve 16"} - ] - } }, { "label": "Avalon auto-sync", "key": "avalon_auto_sync", @@ -109,21 +68,6 @@ "is_hierarchical": true, "group": "avalon", "default": null -}, { - "label": "Tools", - "key": "tools_env", - "type": "enumerator", - "is_hierarchical": true, - "group": "avalon", - "config": { - "multiselect": true, - "data": [ - {"mtoa_3.0.1": "mtoa_3.0.1"}, - {"mtoa_3.1.1": "mtoa_3.1.1"}, - {"mtoa_3.2.0": "mtoa_3.2.0"}, - {"yeti_2.1.2": "yeti_2.1"} - ] - } }, { "label": "Resolution Width", "key": "resolutionWidth", diff --git a/pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json b/pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json deleted file mode 100644 index 6b3a32f181..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "server_url": "", - "api_key": "", - "api_user": "" -} diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/global/modules.json index a28c2f4a03..6c7a45cca3 100644 --- a/pype/settings/defaults/system_settings/global/modules.json +++ b/pype/settings/defaults/system_settings/global/modules.json @@ -27,11 +27,13 @@ "ftrack_events_path": [], "FTRACK_EVENTS_MONGO_DB": "pype", "FTRACK_EVENTS_MONGO_COL": "ftrack_events", - "sync_to_avalon": { - "statuses_name_change": [ - "ready", - "not ready" - ] + "events": { + "sync_to_avalon": { + "enabled": true, + "statuses_name_change": [ + "not ready" + ] + } }, "status_version_to_task": {}, "status_update": { diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index cf95bf4c45..5b19a1159a 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -16,7 +16,8 @@ "is_file": true } ] - }, { + }, + { "type": "dict-invisible", "key": "project_settings", "children": [ diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 87912cfdc0..0ccdd79bc7 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -4,89 +4,6 @@ "key": "plugins", "label": "Plugins", "children": [{ - "type": "dict", - "collapsable": true, - "key": "celaction", - "label": "CelAction", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractCelactionDeadline", - "label": "ExtractCelactionDeadline", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "text", - "key": "deadline_department", - "label": "Deadline apartment" - }, { - "type": "number", - "key": "deadline_priority", - "label": "Deadline priority" - }, { - "type": "text", - "key": "deadline_pool", - "label": "Deadline pool" - }, { - "type": "text", - "key": "deadline_pool_secondary", - "label": "Deadline pool (secondary)" - }, { - "type": "text", - "key": "deadline_group", - "label": "Deadline Group" - }, { - "type": "number", - "key": "deadline_chunk_size", - "label": "Deadline Chunk size" - }] - }] - }] - }, { - "type": "dict", - "collapsable": true, - "key": "ftrack", - "label": "Ftrack", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "IntegrateFtrackNote", - "label": "IntegrateFtrackNote", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "text", - "key": "note_with_intent_template", - "label": "Note with intent template" - }, { - "type": "list", - "object_type": "text", - "key": "note_labels", - "label": "Note labels" - }] - }] - }] - }, { "type": "dict", "collapsable": true, "key": "global", @@ -327,249 +244,6 @@ }] }] }] - }, { - "type": "dict-invisible", - "collapsable": true, - "key": "maya", - "label": "Maya", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "maya", - "label": "Maya", - "children": [ - - { - "type": "schema", - "name": "2_maya_capture" - }, - { - "type": "schema", - "name": "2_maya_plugins" - }, - { - "type": "schema", - "name": "2_maya_workfiles" - } - ] - }] - }, { - "type": "dict", - "collapsable": true, - "key": "nuke", - "label": "Nuke", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "create", - "label": "Create plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": false, - "key": "CreateWriteRender", - "label": "CreateWriteRender", - "is_group": true, - "children": [{ - "type": "text", - "key": "fpath_template", - "label": "Path template" - }] - }, { - "type": "dict", - "collapsable": false, - "key": "CreateWritePrerender", - "label": "CreateWritePrerender", - "is_group": true, - "children": [{ - "type": "text", - "key": "fpath_template", - "label": "Path template" - }] - }] - }, { - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractThumbnail", - "label": "ExtractThumbnail", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "raw-json", - "key": "nodes", - "label": "Nodes" - }] - }, { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ValidateNukeWriteKnobs", - "label": "ValidateNukeWriteKnobs", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "raw-json", - "key": "knobs", - "label": "Knobs" - }] - }, { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataLut", - "label": "ExtractReviewDataLut", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataMov", - "label": "ExtractReviewDataMov", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - }] - }, { - "type": "dict", - "collapsable": true, - "key": "ExtractSlateFrame", - "label": "ExtractSlateFrame", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - }] - }, { - "type": "dict", - "collapsable": true, - "key": "NukeSubmitDeadline", - "label": "NukeSubmitDeadline", - "is_group": true, - "children": [{ - "type": "number", - "key": "deadline_priority", - "label": "deadline_priority" - }, { - "type": "text", - "key": "deadline_pool", - "label": "deadline_pool" - }, { - "type": "text", - "key": "deadline_pool_secondary", - "label": "deadline_pool_secondary" - }, { - "type": "number", - "key": "deadline_chunk_size", - "label": "deadline_chunk_size" - }] - }] - }, { - "type": "raw-json", - "key": "workfile_build", - "label": "Workfile Build logic", - "is_file": true - }] - }, { - "type": "dict", - "collapsable": true, - "key": "nukestudio", - "label": "NukeStudio", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "CollectInstanceVersion", - "label": "Collect Instance Version", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractReviewCutUpVideo", - "label": "Extract Review Cut Up Video", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "object_type": "text", - "key": "tags_addition", - "label": "Tags addition" - }] - }] - }] - }, { - "type": "dict", - "collapsable": true, - "key": "resolve", - "label": "DaVinci Resolve", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "create", - "label": "Creator plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "key": "CreateShotClip", - "label": "Create Shot Clip", - "is_group": true, - "children": [{ - "type": "text", - "key": "clipName", - "label": "Clip name template" - }, { - "type": "text", - "key": "folder", - "label": "Folder" - }, { - "type": "number", - "key": "steps", - "label": "Steps" - }] - } - - ] - }] }, { "type": "dict", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json new file mode 100644 index 0000000000..a1c94b14ef --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json @@ -0,0 +1,50 @@ +{ + "type": "dict", + "collapsable": true, + "key": "celaction", + "label": "CelAction", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "children": [{ + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractCelactionDeadline", + "label": "ExtractCelactionDeadline", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text", + "key": "deadline_department", + "label": "Deadline apartment" + }, { + "type": "number", + "key": "deadline_priority", + "label": "Deadline priority" + }, { + "type": "text", + "key": "deadline_pool", + "label": "Deadline pool" + }, { + "type": "text", + "key": "deadline_pool_secondary", + "label": "Deadline pool (secondary)" + }, { + "type": "text", + "key": "deadline_group", + "label": "Deadline Group" + }, { + "type": "number", + "key": "deadline_chunk_size", + "label": "Deadline Chunk size" + }] + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json new file mode 100644 index 0000000000..1d6b4cbf5b --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json @@ -0,0 +1,129 @@ +{ + "type": "dict", + "key": "Ftrack", + "label": "Ftrack", + "collapsable": true, + "checkbox_key": "enabled", + "is_file": true, + "children": [ + { + "type": "splitter" + }, + { + "type": "label", + "label": "Additional Ftrack paths" + }, + { + "type": "list", + "key": "ftrack_actions_path", + "label": "Action paths", + "object_type": "text" + }, + { + "type": "list", + "key": "ftrack_events_path", + "label": "Event paths", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "events", + "label": "Server Events", + "children": [{ + "type": "dict", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "list", + "key": "statuses_name_change", + "label": "Status name change", + "object_type": { + "type": "text", + "multiline": false + } + }] + }, + { + "type": "dict", + "key": "status_version_to_task", + "label": "Version to Task status mapping", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "dict-modifiable", + "key": "statuses", + "label": "Stausesg", + "object_type": "text" + }] + }, + { + "type": "dict", + "key": "status_update", + "label": "Status Updates", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "statuses", + "type": "dict-invisible", + "children": [ + { + "key": "default", + "type": "text", + "label": "complete" + }, + { + "key": "default2", + "type": "text", + "label": "in progress" + } + ] + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "IntegrateFtrackNote", + "label": "IntegrateFtrackNote", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text", + "key": "note_with_intent_template", + "label": "Note with intent template" + }, { + "type": "list", + "object_type": "text", + "key": "note_labels", + "label": "Note labels" + }] + }] + } + ] + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json new file mode 100644 index 0000000000..d80a6272c5 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json @@ -0,0 +1,43 @@ +{ + "type": "dict", + "collapsable": true, + "key": "hiero", + "label": "Hiero", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "children": [{ + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "CollectInstanceVersion", + "label": "Collect Instance Version", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractReviewCutUpVideo", + "label": "Extract Review Cut Up Video", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "list", + "object_type": "text", + "key": "tags_addition", + "label": "Tags addition" + }] + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json new file mode 100644 index 0000000000..6ef30a349d --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json @@ -0,0 +1,28 @@ +{ + "type": "dict", + "collapsable": true, + "key": "maya", + "label": "Maya", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "key": "maya", + "label": "Maya", + "children": [ + + { + "type": "schema", + "name": "2_maya_capture" + }, + { + "type": "schema", + "name": "2_maya_plugins" + }, + { + "type": "schema", + "name": "2_maya_workfiles" + } + ] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json new file mode 100644 index 0000000000..3151a26b45 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json @@ -0,0 +1,140 @@ +{ + "type": "dict", + "collapsable": true, + "key": "nuke", + "label": "Nuke", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "key": "create", + "label": "Create plugins", + "children": [{ + "type": "dict", + "collapsable": false, + "key": "CreateWriteRender", + "label": "CreateWriteRender", + "is_group": true, + "children": [{ + "type": "text", + "key": "fpath_template", + "label": "Path template" + }] + }, { + "type": "dict", + "collapsable": false, + "key": "CreateWritePrerender", + "label": "CreateWritePrerender", + "is_group": true, + "children": [{ + "type": "text", + "key": "fpath_template", + "label": "Path template" + }] + }] + }, { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "children": [{ + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractThumbnail", + "label": "ExtractThumbnail", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "raw-json", + "key": "nodes", + "label": "Nodes" + }] + }, { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ValidateNukeWriteKnobs", + "label": "ValidateNukeWriteKnobs", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "raw-json", + "key": "knobs", + "label": "Knobs" + }] + }, { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataLut", + "label": "ExtractReviewDataLut", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataMov", + "label": "ExtractReviewDataMov", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + }] + }, { + "type": "dict", + "collapsable": true, + "key": "ExtractSlateFrame", + "label": "ExtractSlateFrame", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + }] + }, { + "type": "dict", + "collapsable": true, + "key": "NukeSubmitDeadline", + "label": "NukeSubmitDeadline", + "is_group": true, + "children": [{ + "type": "number", + "key": "deadline_priority", + "label": "deadline_priority" + }, { + "type": "text", + "key": "deadline_pool", + "label": "deadline_pool" + }, { + "type": "text", + "key": "deadline_pool_secondary", + "label": "deadline_pool_secondary" + }, { + "type": "number", + "key": "deadline_chunk_size", + "label": "deadline_chunk_size" + }] + }] + }, { + "type": "raw-json", + "key": "workfile_build", + "label": "Workfile Build logic" + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json new file mode 100644 index 0000000000..9452911354 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json @@ -0,0 +1,35 @@ +{ + "type": "dict", + "collapsable": true, + "key": "resolve", + "label": "DaVinci Resolve", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "key": "create", + "label": "Creator plugins", + "children": [{ + "type": "dict", + "collapsable": true, + "key": "CreateShotClip", + "label": "Create Shot Clip", + "is_group": true, + "children": [{ + "type": "text", + "key": "clipName", + "label": "Clip name template" + }, { + "type": "text", + "key": "folder", + "label": "Folder" + }, { + "type": "number", + "key": "steps", + "label": "Steps" + }] + } + + ] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json new file mode 100644 index 0000000000..86570e2ac8 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -0,0 +1,147 @@ +{ + "type": "dict", + "key": "Ftrack", + "label": "Ftrack", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "ftrack_server", + "label": "Server" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Additional Ftrack paths" + }, + { + "type": "list", + "key": "ftrack_actions_path", + "label": "Action paths", + "object_type": "text" + }, + { + "type": "list", + "key": "ftrack_events_path", + "label": "Event paths", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Ftrack event server advanced settings" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_DB", + "label": "Event Mongo DB" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_COL", + "label": "Events Mongo Collection" + }, + { + "type": "dict", + "key": "events", + "label": "Server Events", + "children": [{ + "type": "dict", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "list", + "key": "statuses_name_change", + "label": "Status name change", + "object_type": { + "type": "text", + "multiline": false + } + }] + }, + { + "type": "dict", + "key": "status_version_to_task", + "label": "Version to Task status mapping", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "dict-modifiable", + "key": "statuses", + "label": "Stausesg", + "object_type": "text" + }] + }, + { + "type": "dict", + "key": "status_update", + "label": "Status Updates", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "statuses", + "type": "dict-invisible", + "children": [ + { + "key": "default", + "type": "text", + "label": "complete" + }, + { + "key": "default2", + "type": "text", + "label": "in progress" + } + ] + }] + } + ] + }, + { + "key": "intent", + "type": "dict-invisible", + "children": [{ + "type": "dict-modifiable", + "object_type": "text", + "key": "items", + "label": "Intent Key/Label" + }, + { + "key": "default", + "type": "text", + "label": "Default Intent" + } + ] + }, + { + "type": "splitter" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json", + "env_group_key": "ftrack" + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index fa84a27ae3..31eaab2ede 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -5,286 +5,184 @@ "collapsable": true, "is_file": true, "children": [{ - "type": "dict", - "key": "Avalon", - "label": "Avalon", - "collapsable": true, - "children": [{ - "type": "text", - "key": "AVALON_MONGO", - "label": "Avalon Mongo URL" - }, - { - "type": "text", - "key": "AVALON_DB_DATA", - "label": "Avalon Mongo Data Location" - }, - { - "type": "text", - "key": "AVALON_THUMBNAIL_ROOT", - "label": "Thumbnail Storage Location" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "avalon" - } - ] - }, { - "type": "dict", - "key": "Ftrack", - "label": "Ftrack", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "ftrack_server", - "label": "Server" - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Additional Ftrack paths" - }, - { - "type": "list", - "key": "ftrack_actions_path", - "label": "Action paths", - "object_type": "text" - }, - { - "type": "list", - "key": "ftrack_events_path", - "label": "Event paths", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Ftrack event server advanced settings" - }, - { - "type": "text", - "key": "FTRACK_EVENTS_MONGO_DB", - "label": "Event Mongo DB" - }, - { - "type": "text", - "key": "FTRACK_EVENTS_MONGO_COL", - "label": "Events Mongo Collection" - }, - { - "type": "dict", - "key": "sync_to_avalon", - "label": "Sync to avalon", - "children": [{ - "type": "list", - "key": "statuses_name_change", - "label": "Status name change", - "object_type": { - "type": "text", - "multiline": false - } - }] - }, - { - "type": "dict-modifiable", - "key": "status_version_to_task", - "label": "Version to Task status mapping", - "object_type": "text" - }, - { - "type": "dict-modifiable", - "key": "status_update", - "label": "Status Updates", - "object_type": { - "type": "list", - "object_type": "text" + "type": "dict", + "key": "Avalon", + "label": "Avalon", + "collapsable": true, + "children": [{ + "type": "text", + "key": "AVALON_MONGO", + "label": "Avalon Mongo URL" + }, + { + "type": "text", + "key": "AVALON_DB_DATA", + "label": "Avalon Mongo Data Location" + }, + { + "type": "text", + "key": "AVALON_THUMBNAIL_ROOT", + "label": "Thumbnail Storage Location" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json", + "env_group_key": "avalon" } - }, - { - "key": "intent", - "type": "dict-invisible", - "children": [{ - "type": "dict-modifiable", - "object_type": "text", - "key": "items", - "label": "Intent Key/Label" - }, - { - "key": "default", - "type": "text", - "label": "Default Intent" - } - ] - }, - { - "type": "splitter" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "ftrack" - } - ] - }, { - "type": "dict", - "key": "Rest Api", - "label": "Rest Api", - "collapsable": true, - "children": [{ - "type": "number", - "key": "default_port", - "label": "Default Port", - "minimum": 1, - "maximum": 65535 - }, - { - "type": "list", - "key": "exclude_ports", - "label": "Exclude ports", - "object_type": { + ] + }, { + "type": "schema", + "name": "schema_ftrack" + }, + { + "type": "dict", + "key": "Rest Api", + "label": "Rest Api", + "collapsable": true, + "children": [{ "type": "number", + "key": "default_port", + "label": "Default Port", "minimum": 1, "maximum": 65535 + }, + { + "type": "list", + "key": "exclude_ports", + "label": "Exclude ports", + "object_type": { + "type": "number", + "minimum": 1, + "maximum": 65535 + } } - } - ] - }, { - "type": "dict", - "key": "Timers Manager", - "label": "Timers Manager", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ + ] + }, { + "type": "dict", + "key": "Timers Manager", + "label": "Timers Manager", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "number", + "decimal": 2, + "key": "full_time", + "label": "Max idle time" + }, { + "type": "number", + "decimal": 2, + "key": "message_time", + "label": "When dialog will show" + } + ] + }, { + "type": "dict", + "key": "Clockify", + "label": "Clockify", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "workspace_name", + "label": "Workspace name" + } + ] + }, { + "type": "dict", + "key": "Deadline", + "label": "Deadline", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ "type": "boolean", "key": "enabled", "label": "Enabled" - }, - { - "type": "number", - "decimal": 2, - "key": "full_time", - "label": "Max idle time" }, { - "type": "number", - "decimal": 2, - "key": "message_time", - "label": "When dialog will show" - } - ] - }, { - "type": "dict", - "key": "Clockify", - "label": "Clockify", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ + "type": "text", + "key": "DEADLINE_REST_URL", + "label": "Deadline Resl URL" + }] + }, { + "type": "dict", + "key": "Muster", + "label": "Muster", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ "type": "boolean", "key": "enabled", "label": "Enabled" - }, - { + }, { "type": "text", - "key": "workspace_name", - "label": "Workspace name" - } - ] - }, { - "type": "dict", - "key": "Deadline", - "label": "Deadline", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" + "key": "MUSTER_REST_URL", + "label": "Muster Resl URL" + }, { + "type": "dict-modifiable", + "object_type": { + "type": "number", + "minimum": 0, + "maximum": 300 + }, + "is_group": true, + "key": "templates_mapping", + "label": "Templates mapping", + "is_file": true + }] }, { - "type": "text", - "key": "DEADLINE_REST_URL", - "label": "Deadline Resl URL" - }] - }, { - "type": "dict", - "key": "Muster", - "label": "Muster", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" + "type": "dict", + "key": "Logging", + "label": "Logging", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] }, { - "type": "text", - "key": "MUSTER_REST_URL", - "label": "Muster Resl URL" + "type": "dict", + "key": "User setting", + "label": "User setting", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] }, { - "type": "dict-modifiable", - "object_type": { - "type": "number", - "minimum": 0, - "maximum": 300 - }, - "is_group": true, - "key": "templates_mapping", - "label": "Templates mapping", - "is_file": true - }] - }, { - "type": "dict", - "key": "Logging", - "label": "Logging", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "key": "User setting", - "label": "User setting", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "key": "Standalone Publish", - "label": "Standalone Publish", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "key": "Idle Manager", - "label": "Idle Manager", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }] + "type": "dict", + "key": "Standalone Publish", + "label": "Standalone Publish", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "key": "Idle Manager", + "label": "Idle Manager", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + } + ] } From 595bd9022482e9d977ccb65140298644244e9097 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 25 Nov 2020 22:08:16 +0100 Subject: [PATCH 054/219] rename project host settings --- .../projects_schema/0_project_gui_schema.json | 26 +++- ...ns_gui_schema.json => schema_plugins.json} | 0 .../schema_project_celaction.json | 0 .../{schemas => }/schema_project_ftrack.json | 144 +++++++++++------- .../{schemas => }/schema_project_hiero.json | 0 .../{schemas => }/schema_project_maya.json | 6 +- .../{schemas => }/schema_project_nuke.json | 0 .../{schemas => }/schema_project_resolve.json | 0 .../schema_maya_capture.json} | 0 .../schema_maya_plugins.json} | 0 .../schema_maya_workfiles.json} | 0 .../module_settings/schema_ftrack.json | 47 +++--- 12 files changed, 132 insertions(+), 91 deletions(-) rename pype/tools/settings/settings/gui_schemas/projects_schema/{1_plugins_gui_schema.json => schema_plugins.json} (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_celaction.json (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_ftrack.json (54%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_hiero.json (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_maya.json (76%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_nuke.json (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{schemas => }/schema_project_resolve.json (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{2_maya_capture.json => schemas/schema_maya_capture.json} (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{2_maya_plugins.json => schemas/schema_maya_plugins.json} (100%) rename pype/tools/settings/settings/gui_schemas/projects_schema/{2_maya_workfiles.json => schemas/schema_maya_workfiles.json} (100%) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index 5b19a1159a..7409b45533 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -23,7 +23,31 @@ "children": [ { "type": "schema", - "name": "1_plugins_gui_schema" + "name": "schema_project_ftrack" + }, + { + "type": "schema", + "name": "schema_project_maya" + }, + { + "type": "schema", + "name": "schema_project_nuke" + }, + { + "type": "schema", + "name": "schema_project_hiero" + }, + { + "type": "schema", + "name": "schema_project_celaction" + }, + { + "type": "schema", + "name": "schema_project_resolve" + }, + { + "type": "schema", + "name": "schema_plugins" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_celaction.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_celaction.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_celaction.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json similarity index 54% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json index 1d6b4cbf5b..767321f835 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json @@ -32,41 +32,7 @@ "type": "dict", "key": "events", "label": "Server Events", - "children": [{ - "type": "dict", - "key": "sync_to_avalon", - "label": "Sync to avalon", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "key": "statuses_name_change", - "label": "Status name change", - "object_type": { - "type": "text", - "multiline": false - } - }] - }, - { - "type": "dict", - "key": "status_version_to_task", - "label": "Version to Task status mapping", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "dict-modifiable", - "key": "statuses", - "label": "Stausesg", - "object_type": "text" - }] - }, + "children": [ { "type": "dict", "key": "status_update", @@ -96,34 +62,94 @@ }, { "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, + "key": "status_task_to_parent", + "label": "Sync status from Task to Parent", + "checkbox_key": "enabled", "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "IntegrateFtrackNote", - "label": "IntegrateFtrackNote", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "text", - "key": "note_with_intent_template", - "label": "Note with intent template" - }, { - "type": "list", - "object_type": "text", - "key": "note_labels", - "label": "Note labels" - }] + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "key": "status_task_to_version", + "label": "Sync status from Task to Version", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "key": "status_version_to_task", + "label": "Sync status from Version to Task", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "dict-modifiable", + "key": "statuses", + "label": "statuses", + "object_type": "text" + }] + }, + { + "type": "dict", + "key": "first_version_status", + "label": "Set status on first created version", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "key": "next_task_update", + "label": "Update status on next task", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" }] } ] + }, + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [{ + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "IntegrateFtrackNote", + "label": "IntegrateFtrackNote", + "is_group": true, + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text", + "key": "note_with_intent_template", + "label": "Note with intent template" + }, { + "type": "list", + "object_type": "text", + "key": "note_labels", + "label": "Note labels" + }] + }] } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_hiero.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json similarity index 76% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json index 6ef30a349d..38c7ff81a1 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_maya.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json @@ -13,15 +13,15 @@ { "type": "schema", - "name": "2_maya_capture" + "name": "schema_maya_capture" }, { "type": "schema", - "name": "2_maya_plugins" + "name": "schema_maya_plugins" }, { "type": "schema", - "name": "2_maya_workfiles" + "name": "schema_maya_workfiles" } ] }] diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_nuke.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_resolve.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_project_resolve.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_resolve.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_capture.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_capture.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_plugins.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_plugins.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_workfiles.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/2_maya_workfiles.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 86570e2ac8..4ce23c75f7 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -54,7 +54,8 @@ "type": "dict", "key": "events", "label": "Server Events", - "children": [{ + "children": [ + { "type": "dict", "key": "sync_to_avalon", "label": "Sync to avalon", @@ -73,47 +74,37 @@ } }] }, - { + { "type": "dict", - "key": "status_version_to_task", - "label": "Version to Task status mapping", + "key": "push_frame_values_to_task", + "label": "Sync Hierarchical and Entity Attributes", "checkbox_key": "enabled", "children": [{ "type": "boolean", "key": "enabled", "label": "Enabled" - }, { - "type": "dict-modifiable", - "key": "statuses", - "label": "Stausesg", - "object_type": "text" }] }, - { + { "type": "dict", - "key": "status_update", - "label": "Status Updates", + "key": "thumbnail_updates", + "label": "Update Hierarchy thumbnails", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "key": "user_assignment", + "label": "Update Hierarchy thumbnails", "checkbox_key": "enabled", "children": [{ "type": "boolean", "key": "enabled", "label": "Enabled" - }, - { - "key": "statuses", - "type": "dict-invisible", - "children": [ - { - "key": "default", - "type": "text", - "label": "complete" - }, - { - "key": "default2", - "type": "text", - "label": "in progress" - } - ] }] } ] From adb1b8457963dfd3581fb9ad6afccd05778af416 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 23:29:02 +0100 Subject: [PATCH 055/219] storing env metadata in raw json moved back to config_value --- .../settings/settings/widgets/item_types.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index be508d6617..ee15ddda6b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1328,18 +1328,17 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): output = {} for key, value in value.items(): output[key.upper()] = value - - if self.is_environ: - output[METADATA_KEY] = { - "environments": { - self.env_group_key: list(output.keys()) - } - } - return output def config_value(self): - return {self.key: self.item_value()} + value = self.item_value() + if self.is_environ: + value[METADATA_KEY] = { + "environments": { + self.env_group_key: list(value.keys()) + } + } + return {self.key: value} class ListItem(QtWidgets.QWidget, SettingObject): From 7d7561f2a608b58436f14e32fdb146814eef140b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 25 Nov 2020 23:29:33 +0100 Subject: [PATCH 056/219] ModifiableDict adds env metadata to output in config_value if is marked to store env groups keys --- pype/tools/settings/settings/widgets/item_types.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ee15ddda6b..e59db34367 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2334,6 +2334,18 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): output.update(item.config_value()) return output + def config_value(self): + output = self.item_value() + if self.value_is_env_group: + for key, value in tuple(output.items()): + value[METADATA_KEY] = { + "environments": { + key: list(value.keys()) + } + } + output[key] = value + return {self.key: output} + def add_row(self, row=None, key=None, value=None, is_empty=False): # Create new item item_widget = ModifiableDictItem( From 647053273e83a03814401bc9633dd573681f54f5 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 00:01:34 +0100 Subject: [PATCH 057/219] convert ftrack settings --- .../defaults/project_settings/Ftrack.json | 73 +++++++++++++++++++ .../schema_project_ftrack.json | 68 ++++++++++++----- 2 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 pype/settings/defaults/project_settings/Ftrack.json diff --git a/pype/settings/defaults/project_settings/Ftrack.json b/pype/settings/defaults/project_settings/Ftrack.json new file mode 100644 index 0000000000..7c9bb8ce0e --- /dev/null +++ b/pype/settings/defaults/project_settings/Ftrack.json @@ -0,0 +1,73 @@ +{ + "ftrack_actions_path": [], + "ftrack_events_path": [], + "events": { + "status_update": { + "enabled": true, + "mapping": { + "In Progress": [ + "__any__" + ], + "Ready": [ + "Not Ready" + ], + "__ignore__": [ + "in prgoress", + "omitted", + "on hold" + ] + } + }, + "status_task_to_parent": { + "enabled": true, + "parent_status_match_all_task_statuses": { + "Completed": [ + "Approved", + "Omitted" + ] + }, + "parent_status_by_task_status": { + "In Progress": [ + "in progress", + "change requested", + "retake", + "pending review" + ] + } + }, + "status_task_to_version": { + "enabled": true, + "mapping": { + "Approved": [ + "Complete" + ] + } + }, + "status_version_to_task": { + "enabled": true, + "mapping": { + "Complete": [ + "Approved", + "Complete" + ] + } + }, + "first_version_status": { + "enabled": true, + "status": "" + }, + "next_task_update": { + "enabled": true, + "mapping": { + "Ready": "Not Ready" + } + } + }, + "publish": { + "IntegrateFtrackNote": { + "enabled": true, + "note_with_intent_template": "", + "note_labels": [] + } + } +} \ No newline at end of file diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json index 767321f835..a7c835081a 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json @@ -36,7 +36,7 @@ { "type": "dict", "key": "status_update", - "label": "Status Updates", + "label": "Update status on task action", "checkbox_key": "enabled", "children": [{ "type": "boolean", @@ -44,20 +44,12 @@ "label": "Enabled" }, { - "key": "statuses", - "type": "dict-invisible", - "children": [ - { - "key": "default", - "type": "text", - "label": "complete" - }, - { - "key": "default2", - "type": "text", - "label": "in progress" - } - ] + "key": "mapping", + "type": "dict-modifiable", + "object_type": { + "type": "list", + "object_type": "text" + } }] }, { @@ -69,6 +61,24 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + }, + { + "key": "parent_status_match_all_task_statuses", + "type": "dict-modifiable", + "label": "Change parent if all tasks match", + "object_type": { + "type": "list", + "object_type": "text" + } + }, + { + "key": "parent_status_by_task_status", + "type": "dict-modifiable", + "label": "Change parent status if a single task matches", + "object_type": { + "type": "list", + "object_type": "text" + } }] }, { @@ -80,6 +90,13 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + }, { + "type": "dict-modifiable", + "key": "mapping", + "object_type": { + "type": "list", + "object_type": "text" + } }] }, { @@ -93,9 +110,11 @@ "label": "Enabled" }, { "type": "dict-modifiable", - "key": "statuses", - "label": "statuses", - "object_type": "text" + "key": "mapping", + "object_type": { + "type": "list", + "object_type": "text" + } }] }, { @@ -107,7 +126,12 @@ "type": "boolean", "key": "enabled", "label": "Enabled" - }] + },{ + "type": "text", + "key": "status", + "label": "Status" + } + ] }, { "type": "dict", @@ -118,6 +142,12 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + },{ + "type": "dict-modifiable", + "key": "mapping", + "object_type": { + "type": "text" + } }] } ] From 112e1e6aca76713896121957abf9f7da0c52c7b0 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 00:01:55 +0100 Subject: [PATCH 058/219] separate hosts in project settings --- .../defaults/project_settings/celaction.json | 13 ++ .../defaults/project_settings/hiero.json | 11 + .../defaults/project_settings/maya.json | 220 ++++++++++++++++++ .../defaults/project_settings/nuke.json | 62 +++++ .../plugins/maya/workfile_build.json | 17 +- .../defaults/project_settings/resolve.json | 9 + .../system_settings/global/modules.json | 29 ++- 7 files changed, 340 insertions(+), 21 deletions(-) create mode 100644 pype/settings/defaults/project_settings/celaction.json create mode 100644 pype/settings/defaults/project_settings/hiero.json create mode 100644 pype/settings/defaults/project_settings/maya.json create mode 100644 pype/settings/defaults/project_settings/nuke.json create mode 100644 pype/settings/defaults/project_settings/resolve.json diff --git a/pype/settings/defaults/project_settings/celaction.json b/pype/settings/defaults/project_settings/celaction.json new file mode 100644 index 0000000000..10165e92de --- /dev/null +++ b/pype/settings/defaults/project_settings/celaction.json @@ -0,0 +1,13 @@ +{ + "publish": { + "ExtractCelactionDeadline": { + "enabled": true, + "deadline_department": "", + "deadline_priority": 0, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_group": "", + "deadline_chunk_size": 0 + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/hiero.json b/pype/settings/defaults/project_settings/hiero.json new file mode 100644 index 0000000000..8d31f2d56d --- /dev/null +++ b/pype/settings/defaults/project_settings/hiero.json @@ -0,0 +1,11 @@ +{ + "publish": { + "CollectInstanceVersion": { + "enabled": true + }, + "ExtractReviewCutUpVideo": { + "enabled": true, + "tags_addition": [] + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json new file mode 100644 index 0000000000..d9ec13b3ed --- /dev/null +++ b/pype/settings/defaults/project_settings/maya.json @@ -0,0 +1,220 @@ +{ + "maya_capture": { + "Codec": { + "compression": "jpg", + "format": "image", + "quality": 95 + }, + "Display Options": { + "background": [ + 0.7, + 0.7, + 0.7 + ], + "backgroundBottom": [ + 0.7, + 0.7, + 0.7 + ], + "backgroundTop": [ + 0.7, + 0.7, + 0.7 + ], + "override_display": true + }, + "Generic": { + "isolate_view": true, + "off_screen": true + }, + "IO": { + "name": "", + "open_finished": true, + "raw_frame_numbers": true, + "recent_playblasts": [], + "save_file": true + }, + "PanZoom": { + "pan_zoom": true + }, + "Renderer": { + "rendererName": "vp2Renderer" + }, + "Resolution": { + "width": 1080, + "height": 1920, + "percent": 1.0, + "mode": "Custom" + }, + "Time Range": { + "start_frame": 0, + "end_frame": 0, + "frame": "", + "time": "Time Slider" + }, + "Viewport Options": { + "cameras": false, + "clipGhosts": false, + "controlVertices": false, + "deformers": false, + "dimensions": false, + "displayLights": 0, + "dynamicConstraints": false, + "dynamics": false, + "fluids": false, + "follicles": false, + "gpuCacheDisplayFilter": false, + "greasePencils": false, + "grid": false, + "hairSystems": true, + "handles": false, + "high_quality": true, + "hud": false, + "hulls": false, + "ikHandles": false, + "imagePlane": true, + "joints": false, + "lights": false, + "locators": false, + "manipulators": false, + "motionTrails": false, + "nCloths": false, + "nParticles": false, + "nRigids": false, + "nurbsCurves": false, + "nurbsSurfaces": false, + "override_viewport_options": true, + "particleInstancers": false, + "pivots": false, + "planes": false, + "pluginShapes": false, + "polymeshes": true, + "shadows": true, + "strokes": false, + "subdivSurfaces": false, + "textures": false, + "twoSidedLighting": true + }, + "Camera Options": { + "displayGateMask": false, + "displayResolution": false, + "displayFilmGate": false, + "displayFieldChart": false, + "displaySafeAction": false, + "displaySafeTitle": false, + "displayFilmPivot": false, + "displayFilmOrigin": false, + "overscan": 1.0 + } + }, + "publish": { + "ValidateModelName": { + "enabled": true, + "material_file": { + "windows": "", + "darwin": "", + "linux": "" + }, + "regex": "" + }, + "ValidateAssemblyName": { + "enabled": true + }, + "ValidateShaderName": { + "enabled": true, + "regex": "" + }, + "ValidateMeshHasOverlappingUVs": { + "enabled": true + } + }, + "workfile_build": { + "profiles": [ + { + "tasks": [ + "Lighting" + ], + "current_context": [ + { + "subset_name_filters": [ + "\".+[Mm]ain\"" + ], + "families": [ + "model" + ], + "repre_names": [ + "abc", + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "animation", + "pointcache" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "rendersetup" + ], + "repre_names": [ + "json" + ], + "loaders": [ + "RenderSetupLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "camera" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + } + ], + "linked_assets": [ + { + "subset_name_filters": [], + "families": [ + "sedress" + ], + "repre_names": [ + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "ArnoldStandin" + ], + "repre_names": [ + "ass" + ], + "loaders": [ + "assLoader" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json new file mode 100644 index 0000000000..98b4f55aa4 --- /dev/null +++ b/pype/settings/defaults/project_settings/nuke.json @@ -0,0 +1,62 @@ +{ + "create": { + "CreateWriteRender": { + "fpath_template": "" + }, + "CreateWritePrerender": { + "fpath_template": "" + } + }, + "publish": { + "ExtractThumbnail": { + "enabled": true, + "nodes": {} + }, + "ValidateNukeWriteKnobs": { + "enabled": true, + "knobs": {} + }, + "ExtractReviewDataLut": { + "enabled": true + }, + "ExtractReviewDataMov": { + "enabled": true, + "viewer_lut_raw": true + }, + "ExtractSlateFrame": { + "viewer_lut_raw": true + }, + "NukeSubmitDeadline": { + "deadline_priority": 0, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_chunk_size": 0 + } + }, + "workfile_build": { + "profiles": [ + { + "tasks": [ + "compositing" + ], + "current_context": [ + { + "subset_name_filters": [], + "families": [ + "render", + "plate" + ], + "repre_names": [ + "exr", + "dpx" + ], + "loaders": [ + "LoadSequence" + ] + } + ], + "linked_assets": [] + } + ] + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json b/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json index 443bc2cb2c..1aad307895 100644 --- a/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json +++ b/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json @@ -1,10 +1,8 @@ -[ - { +[{ "tasks": [ "lighting" ], - "current_context": [ - { + "current_context": [{ "subset_name_filters": [ ".+[Mm]ain" ], @@ -54,8 +52,7 @@ ] } ], - "linked_assets": [ - { + "linked_assets": [{ "families": [ "setdress" ], @@ -83,8 +80,7 @@ "tasks": [ "animation" ], - "current_context": [ - { + "current_context": [{ "families": [ "camera" ], @@ -108,8 +104,7 @@ ] } ], - "linked_assets": [ - { + "linked_assets": [{ "families": [ "setdress" ], @@ -133,4 +128,4 @@ } ] } -] \ No newline at end of file +] diff --git a/pype/settings/defaults/project_settings/resolve.json b/pype/settings/defaults/project_settings/resolve.json new file mode 100644 index 0000000000..646eb3a70b --- /dev/null +++ b/pype/settings/defaults/project_settings/resolve.json @@ -0,0 +1,9 @@ +{ + "create": { + "CreateShotClip": { + "clipName": "", + "folder": "", + "steps": 0 + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/global/modules.json index 6c7a45cca3..386e37b246 100644 --- a/pype/settings/defaults/system_settings/global/modules.json +++ b/pype/settings/defaults/system_settings/global/modules.json @@ -31,19 +31,28 @@ "sync_to_avalon": { "enabled": true, "statuses_name_change": [ - "not ready" + "not ready", + "ready" ] + }, + "push_frame_values_to_task": { + "enabled": true, + "interest_entity_types": [ + "shot" + ], + "interest_attributess": [ + "frameStart", + "frameEnd" + ] + }, + "thumbnail_updates": { + "enabled": true, + "levels": 2 + }, + "user_assignment": { + "enabled": true } }, - "status_version_to_task": {}, - "status_update": { - "Ready": [ - "Not Ready" - ], - "In Progress": [ - "_any_" - ] - }, "intent": { "items": { "-": "-", From 6259a27659b3a0b10c12ecaffa1fd735468f960a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 00:02:05 +0100 Subject: [PATCH 059/219] fill more host schemas --- .../projects_schema/schema_project_maya.json | 29 +++---- .../projects_schema/schema_project_nuke.json | 8 +- .../schemas/schema_maya_capture.json | 2 +- .../schemas/schema_maya_workfiles.json | 6 -- .../schemas/schema_workfile_build.json | 82 +++++++++++++++++++ .../module_settings/schema_ftrack.json | 76 ++++++++++++----- 6 files changed, 154 insertions(+), 49 deletions(-) delete mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json index 38c7ff81a1..0d8db078ea 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json @@ -5,24 +5,15 @@ "label": "Maya", "is_file": true, "children": [{ - "type": "dict", - "collapsable": true, - "key": "maya", - "label": "Maya", - "children": [ - - { - "type": "schema", - "name": "schema_maya_capture" - }, - { - "type": "schema", - "name": "schema_maya_plugins" - }, - { - "type": "schema", - "name": "schema_maya_workfiles" - } - ] + "type": "schema", + "name": "schema_maya_capture" + }, + { + "type": "schema", + "name": "schema_maya_plugins" + }, + { + "type": "schema", + "name": "schema_workfile_build" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json index 3151a26b45..f928ec6039 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json @@ -132,9 +132,9 @@ "label": "deadline_chunk_size" }] }] - }, { - "type": "raw-json", - "key": "workfile_build", - "label": "Workfile Build logic" + }, + { + "type": "schema", + "name": "schema_workfile_build" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json index 836ad90404..67cd4db496 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json @@ -2,7 +2,7 @@ "type": "dict", "collapsable": true, "key": "maya_capture", - "label": "Maya Capture settings", + "label": "Maya Playblast settings", "is_file": true, "children": [ { diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json deleted file mode 100644 index bae4d32abd..0000000000 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_workfiles.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "raw-json", - "key": "workfile_build", - "label": "Workfile Build logic", - "is_file": true -} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json new file mode 100644 index 0000000000..d8d5b0b09b --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json @@ -0,0 +1,82 @@ +{ + "type": "dict", + "collapsable": true, + "key": "workfile_build", + "label": "Workfile Build Settings", + "is_group": true, + "children": [{ + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [{ + "key": "tasks", + "label": "Tasks", + "type": "list", + "object_type": "text" + }, { + "type": "splitter" + }, { + "key": "current_context", + "label": "Current Context", + "type": "list", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [{ + "key": "subset_name_filters", + "label": "Subset name Filters", + "type": "list", + "object_type": "text" + }, { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + },{ + "key": "repre_names", + "label": "Repre Names", + "type": "list", + "object_type": "text" + },{ + "key": "loaders", + "label": "Loaders", + "type": "list", + "object_type": "text" + }] + } + }, + { + "key": "linked_assets", + "label": "Linked Assets", + "type": "list", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [{ + "key": "subset_name_filters", + "label": "Subset name Filters", + "type": "list", + "object_type": "text" + }, { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + },{ + "key": "repre_names", + "label": "Repre Names", + "type": "list", + "object_type": "text" + },{ + "key": "loaders", + "label": "Loaders", + "type": "list", + "object_type": "text" + }] + } + }] + } + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 4ce23c75f7..9a0d36ad06 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -54,27 +54,32 @@ "type": "dict", "key": "events", "label": "Server Events", - "children": [ - { + "children": [{ "type": "dict", "key": "sync_to_avalon", "label": "Sync to avalon", "checkbox_key": "enabled", "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "key": "statuses_name_change", - "label": "Status name change", - "object_type": { - "type": "text", - "multiline": false + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Allow name and hierarchy change only if following statuses are on all children tasks" + }, + { + "type": "list", + "key": "statuses_name_change", + "label": "Statuses", + "object_type": { + "type": "text", + "multiline": false + } } - }] + ] }, - { + { "type": "dict", "key": "push_frame_values_to_task", "label": "Sync Hierarchical and Entity Attributes", @@ -83,9 +88,25 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + }, { + "type": "list", + "key": "interest_entity_types", + "label": "Entity types of interest", + "object_type": { + "type": "text", + "multiline": false + } + }, { + "type": "list", + "key": "interest_attributess", + "label": "Attributes to sync", + "object_type": { + "type": "text", + "multiline": false + } }] }, - { + { "type": "dict", "key": "thumbnail_updates", "label": "Update Hierarchy thumbnails", @@ -94,12 +115,19 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + },{ + "type": "label", + "label": "Push thumbnail from version, up through multiple hierarchy levels." + },{ + "type": "number", + "key": "levels", + "label": "Levels" }] }, - { + { "type": "dict", "key": "user_assignment", - "label": "Update Hierarchy thumbnails", + "label": "Run script on user assignments", "checkbox_key": "enabled", "children": [{ "type": "boolean", @@ -109,14 +137,24 @@ } ] }, + { + "type": "splitter" + }, { "key": "intent", "type": "dict-invisible", "children": [{ + "type": "label", + "label": "Intent" + }, + { "type": "dict-modifiable", "object_type": "text", - "key": "items", - "label": "Intent Key/Label" + "key": "items" + }, + { + "type": "label", + "label": " " }, { "key": "default", From 8945a5b70f8091e89e50d9017a006a10dafeaf9b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Nov 2020 10:35:47 +0100 Subject: [PATCH 060/219] loader change `fname` on update so it should work as expected now --- pype/plugins/tvpaint/load/load_reference_image.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/tvpaint/load/load_reference_image.py b/pype/plugins/tvpaint/load/load_reference_image.py index 0fa4cefc51..b3cefee4c3 100644 --- a/pype/plugins/tvpaint/load/load_reference_image.py +++ b/pype/plugins/tvpaint/load/load_reference_image.py @@ -162,6 +162,9 @@ class LoadImage(pipeline.Loader): """ # Create new containers first context = get_representation_context(representation) + # Change `fname` to new representation + self.fname = self.filepath_from_context(context) + name = container["name"] namespace = container["namespace"] new_container = self.load(context, name, namespace, {}) From 0cda87bd6351f16eea1c6e6e431f8b24738f1ec5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Nov 2020 13:34:09 +0100 Subject: [PATCH 061/219] feat(imageio): wip schema --- .../schema_anatomy_imageio.json | 274 +++++++++++++++++- 1 file changed, 273 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json index 3fdbd3f7f9..d1a286b883 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json @@ -1,8 +1,280 @@ { "type": "dict", "key": "imageio", - "label": "< ENTER LABEL HERE >", + "label": "Color Management (Image i/o)", "is_file": true, "children": [ + { + "key": "hosts", + "type": "dict", + "label": "Hosts", + "collapsed": false, + "collapsable": true, + "children": [ + { + "key": "hiero", + "type": "dict", + "label": "Hiero", + "children": [ + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsable": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [ + {"nuke-default": "nuke-default"}, + {"aces_1.0.3": "aces_1.0.3"}, + {"aces_1.1": "aces_1.1"}, + {"custom": "custom"} + ] + }, { + "type": "path-widget", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" + } + ] + } + ] + }, { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsable": false, + "children": [ + { + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, { + "key": "nuke", + "type": "dict", + "label": "Nuke", + "children": [ + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsable": false, + "is_group": true, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [ + {"Nuke": "Nuke"}, + {"OCIO": "OCIO"} + ] + }, { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [ + {"nuke-default": "nuke-default"}, + {"spi-vfx": "spi-vfx"}, + {"spi-anim": "spi-anim"}, + {"aces_1.0.3": "aces_0.1.1"}, + {"aces_1.0.3": "aces_0.7.1"}, + {"aces_1.0.3": "aces_1.0.1"}, + {"aces_1.0.3": "aces_1.0.3"}, + {"aces_1.1": "aces_1.1"}, + {"custom": "custom"} + ] + }, { + "type": "path-widget", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, { + "type": "text", + "key": "logLut", + "label": "log files" + }, { + "type": "text", + "key": "floatLut", + "label": "float files" + } + ] + } + ] + }, { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsable": false, + "is_group": true, + "children": [ + { + "key": "CreateWriteRender", + "label": "CreateWriteRender", + "type": "dict", + "children": [ + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, { + "type": "splitter" + }, + { + "key": "knobs", + "label": "Knobs", + "type": "list", + "object_type": { + "type": "dict-item", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, { + "type": "text", + "key": "value", + "label": "Value" + } + ] + } + } + ] + }, { + "type": "list", + "key": "custom-items", + "label": "Custom Nodes", + "object_type": { + "type": "dict", + "children": [ + { + "key": "nodeCreateFamily", + "label": "Creator Family", + "type": "text" + }, { + "type": "text", + "key": "nodeClass", + "label": "Node Class" + }, { + "type": "splitter" + }, + { + "type": "dict-modifiable", + "key": "nodeKnobs", + "label": "Node Knobs", + "highlight_content": true, + "object_type": { + "type": "text", + "key": "nodeKnobValue" + } + } + ] + } + } + ] + }, { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsable": false, + "children": [ + { + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + } + + ] + } ] } From 637605327a365a38d703fe462959e5c2212b0c5e Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 13:43:02 +0100 Subject: [PATCH 062/219] tons of config converted to settings --- .../defaults/project_settings/celaction.json | 4 +- .../ftrack/ftrack_config.json | 16 - .../ftrack/plugins/server.json | 1 - .../project_settings/ftrack/plugins/user.json | 5 - .../defaults/project_settings/global.json | 114 ++++++ .../defaults/project_settings/hiero.json | 12 +- .../defaults/project_settings/maya.json | 93 ++++- .../project_settings/maya/capture.json | 108 ------ .../muster/templates_mapping.json | 19 - .../defaults/project_settings/nuke.json | 46 ++- .../plugins/celaction/publish.json | 11 - .../project_settings/plugins/config.json | 1 - .../plugins/ftrack/publish.json | 7 - .../plugins/global/create.json | 1 - .../plugins/global/filter.json | 1 - .../project_settings/plugins/global/load.json | 1 - .../plugins/global/publish.json | 97 ----- .../project_settings/plugins/maya/create.json | 1 - .../project_settings/plugins/maya/filter.json | 9 - .../project_settings/plugins/maya/load.json | 18 - .../plugins/maya/maya/maya_capture.json | 108 ------ .../plugins/maya/maya/publish.json | 21 -- .../plugins/maya/maya/workfile_build.json | 1 - .../plugins/maya/publish.json | 17 - .../plugins/maya/workfile_build.json | 131 ------- .../project_settings/plugins/nuke/create.json | 8 - .../project_settings/plugins/nuke/load.json | 1 - .../plugins/nuke/publish.json | 53 --- .../plugins/nuke/workfile_build.json | 23 -- .../plugins/nukestudio/filter.json | 10 - .../plugins/nukestudio/publish.json | 9 - .../plugins/resolve/create.json | 7 - .../plugins/standalonepublisher/publish.json | 10 - .../project_settings/plugins/test/create.json | 8 - .../plugins/test/publish.json | 10 - .../defaults/project_settings/resolve.json | 6 +- .../project_settings/standalonepublisher.json | 114 ++++++ .../standalonepublisher/families.json | 90 ----- .../projects_schema/0_project_gui_schema.json | 6 +- .../projects_schema/schema_plugins.json | 243 +------------ .../schema_project_global.json | 343 ++++++++++++++++++ .../projects_schema/schema_project_hiero.json | 5 + .../projects_schema/schema_project_maya.json | 11 +- .../projects_schema/schema_project_nuke.json | 5 + .../schema_project_standalonepublisher.json | 159 ++++++++ .../schemas/schema_maya_load.json | 142 ++++++++ .../schemas/schema_maya_plugins.json | 90 ----- .../schemas/schema_maya_publish.json | 188 ++++++++++ .../schemas/template_color.json | 28 ++ .../schemas/template_creatorfamily.json | 43 +++ 50 files changed, 1302 insertions(+), 1153 deletions(-) delete mode 100644 pype/settings/defaults/project_settings/ftrack/ftrack_config.json delete mode 100644 pype/settings/defaults/project_settings/ftrack/plugins/server.json delete mode 100644 pype/settings/defaults/project_settings/ftrack/plugins/user.json create mode 100644 pype/settings/defaults/project_settings/global.json delete mode 100644 pype/settings/defaults/project_settings/maya/capture.json delete mode 100644 pype/settings/defaults/project_settings/muster/templates_mapping.json delete mode 100644 pype/settings/defaults/project_settings/plugins/celaction/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/config.json delete mode 100644 pype/settings/defaults/project_settings/plugins/ftrack/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/global/create.json delete mode 100644 pype/settings/defaults/project_settings/plugins/global/filter.json delete mode 100644 pype/settings/defaults/project_settings/plugins/global/load.json delete mode 100644 pype/settings/defaults/project_settings/plugins/global/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/create.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/filter.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/load.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/maya/maya_capture.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/maya/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/maya/workfile_build.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/maya/workfile_build.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nuke/create.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nuke/load.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nuke/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nukestudio/filter.json delete mode 100644 pype/settings/defaults/project_settings/plugins/nukestudio/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/resolve/create.json delete mode 100644 pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json delete mode 100644 pype/settings/defaults/project_settings/plugins/test/create.json delete mode 100644 pype/settings/defaults/project_settings/plugins/test/publish.json create mode 100644 pype/settings/defaults/project_settings/standalonepublisher.json delete mode 100644 pype/settings/defaults/project_settings/standalonepublisher/families.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json delete mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_color.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json diff --git a/pype/settings/defaults/project_settings/celaction.json b/pype/settings/defaults/project_settings/celaction.json index 10165e92de..a4a321fb27 100644 --- a/pype/settings/defaults/project_settings/celaction.json +++ b/pype/settings/defaults/project_settings/celaction.json @@ -3,11 +3,11 @@ "ExtractCelactionDeadline": { "enabled": true, "deadline_department": "", - "deadline_priority": 0, + "deadline_priority": 50, "deadline_pool": "", "deadline_pool_secondary": "", "deadline_group": "", - "deadline_chunk_size": 0 + "deadline_chunk_size": 10 } } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/ftrack/ftrack_config.json b/pype/settings/defaults/project_settings/ftrack/ftrack_config.json deleted file mode 100644 index 1ef3a9d69f..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/ftrack_config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sync_to_avalon": { - "statuses_name_change": ["not ready", "ready"] - }, - - "status_update": { - "_ignore_": ["in progress", "ommited", "on hold"], - "Ready": ["not ready"], - "In Progress" : ["_any_"] - }, - "status_version_to_task": { - "__description__": "Status `from` (key) must be lowered!", - "in progress": "in progress", - "approved": "approved" - } -} diff --git a/pype/settings/defaults/project_settings/ftrack/plugins/server.json b/pype/settings/defaults/project_settings/ftrack/plugins/server.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/plugins/server.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/ftrack/plugins/user.json b/pype/settings/defaults/project_settings/ftrack/plugins/user.json deleted file mode 100644 index 1ba8e9b511..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/plugins/user.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "TestAction": { - "ignore_me": true - } -} diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json new file mode 100644 index 0000000000..b68f6d87bd --- /dev/null +++ b/pype/settings/defaults/project_settings/global.json @@ -0,0 +1,114 @@ +{ + "publish": { + "IntegrateMasterVersion": { + "enabled": true + }, + "ExtractJpegEXR": { + "enabled": true, + "ffmpeg_args": { + "input": [], + "output": [] + } + }, + "ExtractReview": { + "enabled": true, + "profiles": [ + { + "families": [], + "hosts": [], + "outputs": { + "h264": { + "ext": "mp4", + "tags": [ + "burnin", + "ftrackreview" + ], + "ffmpeg_args": { + "video_filters": [], + "audio_filters": [], + "input": [ + "gamma 2.2" + ], + "output": [ + "pix_fmt yuv420p", + "crf 18", + "intra" + ] + }, + "filter": { + "families": [ + "render", + "review", + "ftrack" + ] + } + } + } + } + ] + }, + "ExtractBurnin": { + "enabled": true, + "options": { + "font_size": 42, + "opacity": 1, + "bg_opacity": 0, + "x_offset": 5, + "y_offset": 5, + "bg_padding": 5 + }, + "fields": {}, + "profiles": [ + { + "burnins": { + "burnin": { + "TOP_LEFT": "{yy}-{mm}-{dd}", + "TOP_RIGHT": "{anatomy[version]}", + "TOP_CENTERED": "", + "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_CENTERED": "{asset}", + "BOTTOM_LEFT": "{username}" + } + } + } + ] + }, + "IntegrateAssetNew": { + "template_name_profiles": { + "template_name_profiles": { + "publish": { + "families": [], + "tasks": [] + }, + "render": { + "families": [ + "review", + "render", + "prerender" + ] + } + } + } + }, + "ProcessSubmittedJobOnFarm": { + "enabled": true, + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "", + "deadline_chunk_size": "", + "deadline_priority": "", + "aov_filter": { + "maya": [ + ".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*" + ], + "nuke": [], + "aftereffects": [ + ".*" + ], + "celaction": [ + ".*" + ] + } + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/hiero.json b/pype/settings/defaults/project_settings/hiero.json index 8d31f2d56d..e7e6350c29 100644 --- a/pype/settings/defaults/project_settings/hiero.json +++ b/pype/settings/defaults/project_settings/hiero.json @@ -1,11 +1,21 @@ { "publish": { "CollectInstanceVersion": { - "enabled": true + "enabled": false }, "ExtractReviewCutUpVideo": { "enabled": true, "tags_addition": [] } + }, + "filter": { + "strict": { + "ValidateVersion": true, + "VersionUpWorkfile": true + }, + "benevolent": { + "ValidateVersion": false, + "VersionUpWorkfile": false + } } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index d9ec13b3ed..f8315dc129 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -108,6 +108,13 @@ } }, "publish": { + "CollectMayaRender": { + "sync_workfile_version": true + }, + "ValidateCameraAttributes": { + "enabled": true, + "optional": true + }, "ValidateModelName": { "enabled": true, "material_file": { @@ -126,6 +133,89 @@ }, "ValidateMeshHasOverlappingUVs": { "enabled": true + }, + "ExtractCameraAlembic": { + "enabled": true, + "optional": true, + "bake_attributes": [] + }, + "MayaSubmitDeadline": { + "enabled": true, + "tile_assembler_plugin": "DraftTileAssembler" + } + }, + "load": { + "colors": { + "model": [ + 0.0, + 0.0, + 0.0 + ], + "rig": [ + 0.0, + 0.0, + 0.0 + ], + "pointcache": [ + 0.0, + 0.0, + 0.0 + ], + "animation": [ + 0.0, + 0.0, + 0.0 + ], + "ass": [ + 0.0, + 0.0, + 0.0 + ], + "camera": [ + 0.0, + 0.0, + 0.0 + ], + "fbx": [ + 0.0, + 0.0, + 0.0 + ], + "mayaAscii": [ + 0.0, + 0.0, + 0.0 + ], + "setdress": [ + 0.0, + 0.0, + 0.0 + ], + "layout": [ + 0.0, + 0.0, + 0.0 + ], + "vdbcache": [ + 0.0, + 0.0, + 0.0 + ], + "vrayproxy": [ + 0.0, + 0.0, + 0.0 + ], + "yeticache": [ + 0.0, + 0.0, + 0.0 + ], + "yetiRig": [ + 0.0, + 0.0, + 0.0 + ] } }, "workfile_build": { @@ -216,5 +306,6 @@ ] } ] - } + }, + "filter": {} } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/maya/capture.json b/pype/settings/defaults/project_settings/maya/capture.json deleted file mode 100644 index b6c4893034..0000000000 --- a/pype/settings/defaults/project_settings/maya/capture.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "Codec": { - "compression": "jpg", - "format": "image", - "quality": 95 - }, - "Display Options": { - "background": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "backgroundBottom": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "backgroundTop": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "override_display": true - }, - "Generic": { - "isolate_view": true, - "off_screen": true - }, - "IO": { - "name": "", - "open_finished": false, - "raw_frame_numbers": false, - "recent_playblasts": [], - "save_file": false - }, - "PanZoom": { - "pan_zoom": true - }, - "Renderer": { - "rendererName": "vp2Renderer" - }, - "Resolution": { - "height": 1080, - "mode": "Custom", - "percent": 1.0, - "width": 1920 - }, - "Time Range": { - "end_frame": 25, - "frame": "", - "start_frame": 0, - "time": "Time Slider" - }, - "Viewport Options": { - "cameras": false, - "clipGhosts": false, - "controlVertices": false, - "deformers": false, - "dimensions": false, - "displayLights": 0, - "dynamicConstraints": false, - "dynamics": false, - "fluids": false, - "follicles": false, - "gpuCacheDisplayFilter": false, - "greasePencils": false, - "grid": false, - "hairSystems": false, - "handles": false, - "high_quality": true, - "hud": false, - "hulls": false, - "ikHandles": false, - "imagePlane": false, - "joints": false, - "lights": false, - "locators": false, - "manipulators": false, - "motionTrails": false, - "nCloths": false, - "nParticles": false, - "nRigids": false, - "nurbsCurves": false, - "nurbsSurfaces": false, - "override_viewport_options": true, - "particleInstancers": false, - "pivots": false, - "planes": false, - "pluginShapes": false, - "polymeshes": true, - "shadows": false, - "strokes": false, - "subdivSurfaces": false, - "textures": false, - "twoSidedLighting": true - }, - "Camera Options": { - "displayGateMask": false, - "displayResolution": false, - "displayFilmGate": false, - "displayFieldChart": false, - "displaySafeAction": false, - "displaySafeTitle": false, - "displayFilmPivot": false, - "displayFilmOrigin": false, - "overscan": 1.0 - } -} diff --git a/pype/settings/defaults/project_settings/muster/templates_mapping.json b/pype/settings/defaults/project_settings/muster/templates_mapping.json deleted file mode 100644 index 4edab9077d..0000000000 --- a/pype/settings/defaults/project_settings/muster/templates_mapping.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "3delight": 41, - "arnold": 46, - "arnold_sf": 57, - "gelato": 30, - "harware": 3, - "krakatoa": 51, - "file_layers": 7, - "mentalray": 2, - "mentalray_sf": 6, - "redshift": 55, - "renderman": 29, - "software": 1, - "software_sf": 5, - "turtle": 10, - "vector": 4, - "vray": 37, - "ffmpeg": 48 -} diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json index 98b4f55aa4..5b9ef5c21c 100644 --- a/pype/settings/defaults/project_settings/nuke.json +++ b/pype/settings/defaults/project_settings/nuke.json @@ -1,36 +1,63 @@ { "create": { "CreateWriteRender": { - "fpath_template": "" + "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" }, "CreateWritePrerender": { - "fpath_template": "" + "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" } }, "publish": { "ExtractThumbnail": { "enabled": true, - "nodes": {} + "nodes": { + "Reformat": [ + [ + "type", + "to format" + ], + [ + "format", + "HD_1080" + ], + [ + "filter", + "Lanczos6" + ], + [ + "black_outside", + true + ], + [ + "pbb", + false + ] + ] + } }, "ValidateNukeWriteKnobs": { "enabled": true, - "knobs": {} + "knobs": { + "render": { + "review": true + } + } }, "ExtractReviewDataLut": { "enabled": true }, "ExtractReviewDataMov": { "enabled": true, - "viewer_lut_raw": true + "viewer_lut_raw": false }, "ExtractSlateFrame": { - "viewer_lut_raw": true + "viewer_lut_raw": false }, "NukeSubmitDeadline": { - "deadline_priority": 0, + "deadline_priority": 50, "deadline_pool": "", "deadline_pool_secondary": "", - "deadline_chunk_size": 0 + "deadline_chunk_size": 1 } }, "workfile_build": { @@ -58,5 +85,6 @@ "linked_assets": [] } ] - } + }, + "filter": {} } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/celaction/publish.json b/pype/settings/defaults/project_settings/plugins/celaction/publish.json deleted file mode 100644 index 4cda2d5656..0000000000 --- a/pype/settings/defaults/project_settings/plugins/celaction/publish.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ExtractCelactionDeadline": { - "enabled": true, - "deadline_department": "", - "deadline_priority": 60, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_group": "", - "deadline_chunk_size": 10 - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/config.json b/pype/settings/defaults/project_settings/plugins/config.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/pype/settings/defaults/project_settings/plugins/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/ftrack/publish.json b/pype/settings/defaults/project_settings/plugins/ftrack/publish.json deleted file mode 100644 index 8570a400e8..0000000000 --- a/pype/settings/defaults/project_settings/plugins/ftrack/publish.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "IntegrateFtrackNote": { - "enabled": true, - "note_with_intent_template": "{intent}: {comment}", - "note_labels": [] - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/global/create.json b/pype/settings/defaults/project_settings/plugins/global/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/plugins/global/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/plugins/global/filter.json b/pype/settings/defaults/project_settings/plugins/global/filter.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/plugins/global/filter.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/plugins/global/load.json b/pype/settings/defaults/project_settings/plugins/global/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/plugins/global/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/plugins/global/publish.json b/pype/settings/defaults/project_settings/plugins/global/publish.json deleted file mode 100644 index 676985797f..0000000000 --- a/pype/settings/defaults/project_settings/plugins/global/publish.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "IntegrateMasterVersion": { - "enabled": false - }, - "ExtractJpegEXR": { - "enabled": true, - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "output": [] - } - }, - "ExtractReview": { - "enabled": true, - "profiles": [ - { - "families": [], - "hosts": [], - "outputs": { - "h264": { - "ext": "mp4", - "tags": [ - "burnin", - "ftrackreview" - ], - "ffmpeg_args": { - "video_filters": [], - "audio_filters": [], - "input": [ - "-gamma 2.2" - ], - "output": [ - "-pix_fmt yuv420p", - "-crf 18", - "-intra" - ] - }, - "filter": { - "families": [ - "render", - "review", - "ftrack" - ] - } - } - } - } - ] - }, - "ExtractBurnin": { - "enabled": true, - "options": { - "font_size": 42, - "opacity": 1, - "bg_opacity": 0, - "x_offset": 5, - "y_offset": 5, - "bg_padding": 5 - }, - "profiles": [ - { - "burnins": { - "burnin": { - "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_RIGHT": "{anatomy[version]}", - "TOP_CENTERED": "", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", - "BOTTOM_CENTERED": "{asset}", - "BOTTOM_LEFT": "{username}" - } - } - } - ] - }, - "IntegrateAssetNew": { - "template_name_profiles": { - "publish": { - "families": [], - "tasks": [] - }, - "render": { - "families": [ - "review", - "render", - "prerender" - ] - } - } - }, - "ProcessSubmittedJobOnFarm": { - "enabled": true, - "deadline_department": "", - "deadline_pool": "", - "deadline_group": "" - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/create.json b/pype/settings/defaults/project_settings/plugins/maya/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/plugins/maya/filter.json b/pype/settings/defaults/project_settings/plugins/maya/filter.json deleted file mode 100644 index 83d6f05f31..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/filter.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Preset n1": { - "ValidateNoAnimation": false, - "ValidateShapeDefaultNames": false - }, - "Preset n2": { - "ValidateNoAnimation": false - } -} diff --git a/pype/settings/defaults/project_settings/plugins/maya/load.json b/pype/settings/defaults/project_settings/plugins/maya/load.json deleted file mode 100644 index 260fbb35ee..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/load.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "colors": { - "model": [0.821, 0.518, 0.117], - "rig": [0.144, 0.443, 0.463], - "pointcache": [0.368, 0.821, 0.117], - "animation": [0.368, 0.821, 0.117], - "ass": [1.0, 0.332, 0.312], - "camera": [0.447, 0.312, 1.0], - "fbx": [1.0, 0.931, 0.312], - "mayaAscii": [0.312, 1.0, 0.747], - "setdress": [0.312, 1.0, 0.747], - "layout": [0.312, 1.0, 0.747], - "vdbcache": [0.312, 1.0, 0.428], - "vrayproxy": [0.258, 0.95, 0.541], - "yeticache": [0.2, 0.8, 0.3], - "yetiRig": [0, 0.8, 0.5] - } -} diff --git a/pype/settings/defaults/project_settings/plugins/maya/maya/maya_capture.json b/pype/settings/defaults/project_settings/plugins/maya/maya/maya_capture.json deleted file mode 100644 index 02e6a9b95d..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/maya/maya_capture.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "Codec": { - "compression": "jpg", - "format": "image", - "quality": 95 - }, - "Display Options": { - "background": [ - 0.714, - 0.714, - 0.714 - ], - "backgroundBottom": [ - 0.714, - 0.714, - 0.714 - ], - "backgroundTop": [ - 0.714, - 0.714, - 0.714 - ], - "override_display": true - }, - "Generic": { - "isolate_view": true, - "off_screen": true - }, - "IO": { - "name": "", - "open_finished": false, - "raw_frame_numbers": false, - "recent_playblasts": [], - "save_file": false - }, - "PanZoom": { - "pan_zoom": true - }, - "Renderer": { - "rendererName": "vp2Renderer" - }, - "Resolution": { - "width": 1920, - "height": 1080, - "percent": 1.0, - "mode": "Custom" - }, - "Time Range": { - "start_frame": 0, - "end_frame": 25, - "frame": "", - "time": "Time Slider" - }, - "Viewport Options": { - "cameras": false, - "clipGhosts": false, - "controlVertices": false, - "deformers": false, - "dimensions": false, - "displayLights": 0, - "dynamicConstraints": false, - "dynamics": false, - "fluids": false, - "follicles": false, - "gpuCacheDisplayFilter": false, - "greasePencils": false, - "grid": false, - "hairSystems": false, - "handles": false, - "high_quality": true, - "hud": false, - "hulls": false, - "ikHandles": false, - "imagePlane": false, - "joints": false, - "lights": false, - "locators": false, - "manipulators": false, - "motionTrails": false, - "nCloths": false, - "nParticles": false, - "nRigids": false, - "nurbsCurves": false, - "nurbsSurfaces": false, - "override_viewport_options": true, - "particleInstancers": false, - "pivots": false, - "planes": false, - "pluginShapes": false, - "polymeshes": true, - "shadows": false, - "strokes": false, - "subdivSurfaces": false, - "textures": false, - "twoSidedLighting": true - }, - "Camera Options": { - "displayGateMask": false, - "displayResolution": false, - "displayFilmGate": false, - "displayFieldChart": false, - "displaySafeAction": false, - "displaySafeTitle": false, - "displayFilmPivot": false, - "displayFilmOrigin": false, - "overscan": 1.0 - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/maya/publish.json b/pype/settings/defaults/project_settings/plugins/maya/maya/publish.json deleted file mode 100644 index 486f0917e2..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/maya/publish.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "ValidateModelName": { - "enabled": true, - "material_file": { - "windows": "", - "darwin": "", - "linux": "" - }, - "regex": "" - }, - "ValidateAssemblyName": { - "enabled": true - }, - "ValidateShaderName": { - "enabled": true, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": true - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/maya/workfile_build.json b/pype/settings/defaults/project_settings/plugins/maya/maya/workfile_build.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/maya/workfile_build.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/publish.json b/pype/settings/defaults/project_settings/plugins/maya/publish.json deleted file mode 100644 index 2b3637ff80..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/publish.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ValidateModelName": { - "enabled": false, - "material_file": "/path/to/shader_name_definition.txt", - "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" - }, - "ValidateAssemblyName": { - "enabled": false - }, - "ValidateShaderName": { - "enabled": false, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": false - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json b/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json deleted file mode 100644 index 1aad307895..0000000000 --- a/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json +++ /dev/null @@ -1,131 +0,0 @@ -[{ - "tasks": [ - "lighting" - ], - "current_context": [{ - "subset_name_filters": [ - ".+[Mm]ain" - ], - "families": [ - "model" - ], - "repre_names": [ - "abc", - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "families": [ - "animation", - "pointcache" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "families": [ - "rendersetup" - ], - "repre_names": [ - "json" - ], - "loaders": [ - "RenderSetupLoader" - ] - }, - { - "families": [ - "camera" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - } - ], - "linked_assets": [{ - "families": [ - "setdress" - ], - "repre_names": [ - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "families": [ - "ass" - ], - "repre_names": [ - "ass" - ], - "loaders": [ - "assLoader" - ] - } - ] - }, - { - "tasks": [ - "animation" - ], - "current_context": [{ - "families": [ - "camera" - ], - "repre_names": [ - "abc", - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "families": [ - "audio" - ], - "repre_names": [ - "wav" - ], - "loaders": [ - "RenderSetupLoader" - ] - } - ], - "linked_assets": [{ - "families": [ - "setdress" - ], - "repre_names": [ - "proxy" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "families": [ - "rig" - ], - "repre_names": [ - "ass" - ], - "loaders": [ - "rigLoader" - ] - } - ] - } -] diff --git a/pype/settings/defaults/project_settings/plugins/nuke/create.json b/pype/settings/defaults/project_settings/plugins/nuke/create.json deleted file mode 100644 index 79ab665696..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nuke/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CreateWriteRender": { - "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" - }, - "CreateWritePrerender": { - "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/nuke/load.json b/pype/settings/defaults/project_settings/plugins/nuke/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nuke/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/settings/defaults/project_settings/plugins/nuke/publish.json b/pype/settings/defaults/project_settings/plugins/nuke/publish.json deleted file mode 100644 index 50b5b27fc5..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nuke/publish.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "ExtractThumbnail": { - "enabled": true, - "nodes": { - "Reformat": [ - [ - "type", - "to format" - ], - [ - "format", - "HD_1080" - ], - [ - "filter", - "Lanczos6" - ], - [ - "black_outside", - true - ], - [ - "pbb", - false - ] - ] - } - }, - "ValidateNukeWriteKnobs": { - "enabled": true, - "knobs": { - "render": { - "review": true - } - } - }, - "ExtractReviewDataLut": { - "enabled": true - }, - "ExtractReviewDataMov": { - "enabled": true, - "viewer_lut_raw": false - }, - "ExtractSlateFrame": { - "viewer_lut_raw": false - }, - "NukeSubmitDeadline": { - "deadline_priority": 50, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_chunk_size": 1 - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json b/pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json deleted file mode 100644 index 4b48b46184..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "tasks": [ - "compositing" - ], - "current_context": [ - { - "families": [ - "render", - "plate" - ], - "repre_names": [ - "exr", - "dpx" - ], - "loaders": [ - "LoadSequence" - ] - } - ], - "linked_assets": [] - } -] \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/nukestudio/filter.json b/pype/settings/defaults/project_settings/plugins/nukestudio/filter.json deleted file mode 100644 index bd6a0dc1bd..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nukestudio/filter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "strict": { - "ValidateVersion": true, - "VersionUpWorkfile": true - }, - "benevolent": { - "ValidateVersion": false, - "VersionUpWorkfile": false - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/nukestudio/publish.json b/pype/settings/defaults/project_settings/plugins/nukestudio/publish.json deleted file mode 100644 index d99a878c35..0000000000 --- a/pype/settings/defaults/project_settings/plugins/nukestudio/publish.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "CollectInstanceVersion": { - "enabled": false - }, - "ExtractReviewCutUpVideo": { - "enabled": true, - "tags_addition": [] - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/resolve/create.json b/pype/settings/defaults/project_settings/plugins/resolve/create.json deleted file mode 100644 index 8ff5b15714..0000000000 --- a/pype/settings/defaults/project_settings/plugins/resolve/create.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "CreateShotClip": { - "clipName": "{track}{sequence}{shot}", - "folder": "takes", - "steps": 20 - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json b/pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json deleted file mode 100644 index f7699ef9f7..0000000000 --- a/pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ExtractThumbnailSP": { - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "output": [] - } - } -} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/plugins/test/create.json b/pype/settings/defaults/project_settings/plugins/test/create.json deleted file mode 100644 index fa0b2fc05f..0000000000 --- a/pype/settings/defaults/project_settings/plugins/test/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "MyTestCreator": { - "my_test_property": "B", - "active": false, - "new_property": "new", - "family": "new_family" - } -} diff --git a/pype/settings/defaults/project_settings/plugins/test/publish.json b/pype/settings/defaults/project_settings/plugins/test/publish.json deleted file mode 100644 index 3180dd5d8a..0000000000 --- a/pype/settings/defaults/project_settings/plugins/test/publish.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "MyTestPlugin": { - "label": "loaded from preset", - "optional": true, - "families": ["changed", "by", "preset"] - }, - "MyTestRemovedPlugin": { - "enabled": false - } -} diff --git a/pype/settings/defaults/project_settings/resolve.json b/pype/settings/defaults/project_settings/resolve.json index 646eb3a70b..cb7064ee76 100644 --- a/pype/settings/defaults/project_settings/resolve.json +++ b/pype/settings/defaults/project_settings/resolve.json @@ -1,9 +1,9 @@ { "create": { "CreateShotClip": { - "clipName": "", - "folder": "", - "steps": 0 + "clipName": "{track}{sequence}{shot}", + "folder": "takes", + "steps": 20 } } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json new file mode 100644 index 0000000000..21ea3db586 --- /dev/null +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -0,0 +1,114 @@ +{ + "publish": { + "ExtractThumbnailSP": { + "ffmpeg_args": { + "input": [ + "gamma 2.2" + ], + "output": [] + } + } + }, + "create": { + "create_workfile": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_model": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_look": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_rig": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_pointcache": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_plate": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_camera": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_mayaAscii": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_editorial": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_image": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "create_matchmove": { + "name": "", + "label": "", + "family": "", + "icon": "", + "defaults": [], + "help": "" + }, + "other_families": { + "create_background": { + "name": "background", + "label": "Background", + "family": "background", + "icon": "image", + "defaults": [ + "Main" + ], + "help": "Layered background from PSD." + } + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/standalonepublisher/families.json b/pype/settings/defaults/project_settings/standalonepublisher/families.json deleted file mode 100644 index d05941cc26..0000000000 --- a/pype/settings/defaults/project_settings/standalonepublisher/families.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "create_look": { - "name": "look", - "label": "Look", - "family": "look", - "icon": "paint-brush", - "defaults": ["Main"], - "help": "Shader connections defining shape look" - }, - "create_model": { - "name": "model", - "label": "Model", - "family": "model", - "icon": "cube", - "defaults": ["Main", "Proxy", "Sculpt"], - "help": "Polygonal static geometry" - }, - "create_workfile": { - "name": "workfile", - "label": "Workfile", - "family": "workfile", - "icon": "cube", - "defaults": ["Main"], - "help": "Working scene backup" - }, - "create_camera": { - "name": "camera", - "label": "Camera", - "family": "camera", - "icon": "video-camera", - "defaults": ["Main"], - "help": "Single baked camera" - }, - "create_pointcache": { - "name": "pointcache", - "label": "Pointcache", - "family": "pointcache", - "icon": "gears", - "defaults": ["Main"], - "help": "Alembic pointcache for animated data" - }, - "create_rig": { - "name": "rig", - "label": "Rig", - "family": "rig", - "icon": "wheelchair", - "defaults": ["Main"], - "help": "Artist-friendly rig with controls" - }, - "create_layout": { - "name": "layout", - "label": "Layout", - "family": "layout", - "icon": "cubes", - "defaults": ["Main"], - "help": "Simple scene for animators with camera" - }, - "create_plate": { - "name": "plate", - "label": "Plate", - "family": "plate", - "icon": "camera", - "defaults": ["Main", "BG", "Reference"], - "help": "Plates for compositors" - }, - "create_matchmove": { - "name": "matchmove", - "label": "Matchmove script", - "family": "matchmove", - "icon": "empire", - "defaults": ["Camera", "Object", "Mocap"], - "help": "Script exported from matchmoving application" - }, - "create_images": { - "name": "image", - "label": "Image file", - "family": "image", - "icon": "image", - "defaults": ["ConceptArt", "Reference", "Texture", "MattePaint"], - "help": "Holder for all kinds of image data" - }, - "create_editorial": { - "name": "editorial", - "label": "Editorial", - "family": "editorial", - "icon": "image", - "defaults": ["Main"], - "help": "Editorial files to generate shots." - } -} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index 7409b45533..0c14ff6f58 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -21,6 +21,10 @@ "type": "dict-invisible", "key": "project_settings", "children": [ + { + "type": "schema", + "name": "schema_project_global" + }, { "type": "schema", "name": "schema_project_ftrack" @@ -47,7 +51,7 @@ }, { "type": "schema", - "name": "schema_plugins" + "name": "schema_project_standalonepublisher" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json index 0ccdd79bc7..f04750ff83 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_plugins.json @@ -3,248 +3,7 @@ "collapsable": true, "key": "plugins", "label": "Plugins", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "global", - "label": "Global", - "children": [{ - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "IntegrateMasterVersion", - "label": "IntegrateMasterVersion", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractJpegEXR", - "label": "ExtractJpegEXR", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "dict-invisible", - "key": "ffmpeg_args", - "children": [{ - "type": "list", - "object_type": "text", - "key": "input", - "label": "FFmpeg input arguments" - }, { - "type": "list", - "object_type": "text", - "key": "output", - "label": "FFmpeg output arguments" - }] - }] - }, { - "type": "dict", - "collapsable": true, - "key": "ExtractReview", - "label": "ExtractReview", - "checkbox_key": "enabled", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "key": "profiles", - "label": "Profiles", - "object_type": { - "type": "dict", - "children": [{ - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }, { - "key": "hosts", - "label": "Hosts", - "type": "list", - "object_type": "text" - }, { - "type": "splitter" - }, { - "key": "outputs", - "label": "Output Definitions", - "type": "dict-modifiable", - "highlight_content": true, - "object_type": { - "type": "dict", - "children": [{ - "key": "ext", - "label": "Output extension", - "type": "text" - }, { - "key": "tags", - "label": "Tags", - "type": "enum", - "multiselection": true, - "enum_items": [{ - "burnin": "Add burnins" - }, - { - "ftrackreview": "Add to Ftrack" - }, - { - "delete": "Delete output" - }, - { - "slate-frame": "Add slate frame" - }, - { - "no-hnadles": "Skip handle frames" - } - ] - }, { - "key": "ffmpeg_args", - "label": "FFmpeg arguments", - "type": "dict", - "highlight_content": true, - "children": [{ - "key": "video_filters", - "label": "Video filters", - "type": "list", - "object_type": "text" - }, { - "type": "splitter" - }, { - "key": "audio_filters", - "label": "Audio filters", - "type": "list", - "object_type": "text" - }, { - "type": "splitter" - }, { - "key": "input", - "label": "Input arguments", - "type": "list", - "object_type": "text" - }, { - "type": "splitter" - }, { - "key": "output", - "label": "Output arguments", - "type": "list", - "object_type": "text" - }] - }, { - "key": "filter", - "label": "Additional output filtering", - "type": "dict", - "highlight_content": true, - "children": [{ - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }] - }] - } - }] - } - }] - }, { - "type": "dict", - "collapsable": true, - "key": "ExtractBurnin", - "label": "ExtractBurnin", - "checkbox_key": "enabled", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "dict", - "collapsable": true, - "key": "options", - "label": "Burnin formating options", - "children": [{ - "type": "number", - "key": "font_size", - "label": "Font size" - }, { - "type": "number", - "key": "opacity", - "label": "Font opacity" - }, { - "type": "number", - "key": "bg_opacity", - "label": "Background opacity" - }, { - "type": "number", - "key": "x_offset", - "label": "X Offset" - }, { - "type": "number", - "key": "y_offset", - "label": "Y Offset" - }, { - "type": "number", - "key": "bg_padding", - "label": "Padding aroung text" - }] - }, { - "type": "raw-json", - "key": "profiles", - "label": "Burnin profiles" - }] - }, { - "type": "dict", - "collapsable": true, - "key": "IntegrateAssetNew", - "label": "IntegrateAssetNew", - "is_group": true, - "children": [{ - "type": "raw-json", - "key": "template_name_profiles", - "label": "template_name_profiles" - }] - }, { - "type": "dict", - "collapsable": true, - "key": "ProcessSubmittedJobOnFarm", - "label": "ProcessSubmittedJobOnFarm", - "checkbox_key": "enabled", - "is_group": true, - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "text", - "key": "deadline_department", - "label": "Deadline department" - }, { - "type": "text", - "key": "deadline_pool", - "label": "Deadline Pool" - }, { - "type": "text", - "key": "deadline_group", - "label": "Deadline Group" - }] - }] - }] - }, + "children": [ { "type": "dict", "collapsable": true, diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json new file mode 100644 index 0000000000..ef8fc58257 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json @@ -0,0 +1,343 @@ +{ + "type": "dict", + "collapsable": true, + "key": "global", + "label": "Global", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "IntegrateMasterVersion", + "label": "IntegrateMasterVersion", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractJpegEXR", + "label": "ExtractJpegEXR", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict-invisible", + "key": "ffmpeg_args", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "input", + "label": "FFmpeg input arguments" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "FFmpeg output arguments" + }] + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractReview", + "label": "ExtractReview", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": + { + "type": "dict", + "children": [ + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "tags", + "label": "Tags", + "type": "enum", + "multiselection": true, + "enum_items": [ + { + "burnin": "Add burnins" + }, + { + "ftrackreview": "Add to Ftrack" + }, + { + "delete": "Delete output" + }, + { + "slate-frame": "Add slate frame" + }, + { + "no-hnadles": "Skip handle frames" + }] + }, + { + "key": "ffmpeg_args", + "label": "FFmpeg arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "video_filters", + "label": "Video filters", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "audio_filters", + "label": "Audio filters", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "input", + "label": "Input arguments", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "output", + "label": "Output arguments", + "type": "list", + "object_type": "text" + }] + }, + { + "key": "filter", + "label": "Additional output filtering", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }] + }] + } + }] + } + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractBurnin", + "label": "ExtractBurnin", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict", + "collapsable": true, + "key": "options", + "label": "Burnin formating options", + "children": [ + { + "type": "number", + "key": "font_size", + "label": "Font size" + }, + { + "type": "number", + "key": "opacity", + "label": "Font opacity" + }, + { + "type": "number", + "key": "bg_opacity", + "label": "Background opacity" + }, + { + "type": "number", + "key": "x_offset", + "label": "X Offset" + }, + { + "type": "number", + "key": "y_offset", + "label": "Y Offset" + }, + { + "type": "number", + "key": "bg_padding", + "label": "Padding aroung text" + }] + }, + { + "type": "raw-json", + "key": "fields", + "label": "Burnin Fields" + }, + { + "type": "raw-json", + "key": "profiles", + "label": "Burnin profiles" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "IntegrateAssetNew", + "label": "IntegrateAssetNew", + "is_group": true, + "children": [ + { + "type": "raw-json", + "key": "template_name_profiles", + "label": "template_name_profiles" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ProcessSubmittedJobOnFarm", + "label": "ProcessSubmittedJobOnFarm", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "deadline_department", + "label": "Deadline department" + }, + { + "type": "text", + "key": "deadline_pool", + "label": "Deadline Pool" + }, + { + "type": "text", + "key": "deadline_group", + "label": "Deadline Group" + }, + { + "type": "text", + "key": "deadline_chunk_size", + "label": "Deadline Chunk Size" + }, + { + "type": "text", + "key": "deadline_priority", + "label": "Deadline Priotity" + }, + { + "type": "dict", + "key": "aov_filter", + "label": "Reviewable subsets filter", + "children": [ + { + "type": "list", + "key": "maya", + "label": "Maya", + "object_type":{ + "type":"text" + } + }, + { + "type": "list", + "key": "nuke", + "label": "Nuke", + "object_type":{ + "type":"text" + } + }, + { + "type": "list", + "key": "aftereffects", + "label": "After Effects", + "object_type":{ + "type":"text" + } + }, + { + "type": "list", + "key": "celaction", + "label": "Celaction", + "object_type":{ + "type":"text" + } + } + ] + }] + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json index d80a6272c5..0f8475655c 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json @@ -39,5 +39,10 @@ "label": "Tags addition" }] }] + }, + { + "type": "raw-json", + "key": "filter", + "label": "Publish GUI Filters" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json index 0d8db078ea..d863941d1d 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json @@ -10,10 +10,19 @@ }, { "type": "schema", - "name": "schema_maya_plugins" + "name": "schema_maya_publish" + }, + { + "type": "schema", + "name": "schema_maya_load" }, { "type": "schema", "name": "schema_workfile_build" + }, + { + "type": "raw-json", + "key": "filter", + "label": "Publish GUI Filters" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json index f928ec6039..48d78cc422 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json @@ -136,5 +136,10 @@ { "type": "schema", "name": "schema_workfile_build" + }, + { + "type": "raw-json", + "key": "filter", + "label": "Publish GUI Filters" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json new file mode 100644 index 0000000000..5e07d82de6 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json @@ -0,0 +1,159 @@ +{ + "type": "dict", + "collapsable": true, + "key": "standalonepublisher", + "label": "Standalone Publisher", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "ExtractThumbnailSP", + "label": "ExtractThumbnailSP", + "is_group": true, + "children": [ + { + "type": "dict", + "collapsable": false, + "key": "ffmpeg_args", + "label": "ffmpeg_args", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "input", + "label": "input" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "output" + }] + }] + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "create", + "label": "Creator plugins", + "is_file": true, + "children": [ + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Workfile", + "name": "workfile" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Model", + "name": "model" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Look", + "name": "look" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Rig", + "name": "rig" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Pointcache", + "name": "pointcache" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Plate", + "name": "plate" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Camera", + "name": "camera" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Maya Scene", + "name": "mayaAscii" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Editorial", + "name": "editorial" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Image", + "name": "image" + }] + }, + { + "type": "schema_template", + "name": "template_creatorfamily", + "template_data": [ + { + "label": "Matchmove", + "name": "matchmove" + }] + }, + { + "type": "dict-modifiable", + "key": "other_families", + "label": "Other Families", + "object_type": { + "type": "raw-json" + } + }] + }] + +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json new file mode 100644 index 0000000000..0b96e63124 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json @@ -0,0 +1,142 @@ +{ + "type": "dict", + "collapsable": true, + "key": "load", + "label": "Loader plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "colors", + "label": "Loaded Subsets Outliner Colors", + "children": [ + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Model", + "name": "model" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Rig", + "name": "rig" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Pointcache", + "name": "pointcache" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Animation", + "name": "animation" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Arnold Standin", + "name": "ass" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Camera", + "name": "camera" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "FBX", + "name": "fbx" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Maya Scene", + "name": "mayaAscii" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Set Dress", + "name": "setdress" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Layout", + "name": "layout" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "VDB Cache", + "name": "vdbcache" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Vray Proxy", + "name": "vrayproxy" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Yeti Cache", + "name": "yeticache" + }] + }, + { + "type": "schema_template", + "name": "template_color", + "template_data": [ + { + "label": "Yeti Rig", + "name": "yetiRig" + }] + } + ] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json deleted file mode 100644 index 7ba9608610..0000000000 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_plugins.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [ - { - "type": "dict", - "collapsable": true, - "key": "ValidateModelName", - "label": "Validate Model Name", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" - }, - { - "type": "path-widget", - "key": "material_file", - "label": "Material File", - "multiplatform": true, - "multipath": false - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] - }, { - "type": "dict", - "collapsable": true, - "key": "ValidateAssemblyName", - "label": "Validate Assembly Name", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, { - "type": "dict", - "collapsable": true, - "key": "ValidateShaderName", - "label": "ValidateShaderName", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "label", - "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" - - }, { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] - }, { - "type": "dict", - "collapsable": true, - "key": "ValidateMeshHasOverlappingUVs", - "label": "ValidateMeshHasOverlappingUVs", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - } - ] - } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json new file mode 100644 index 0000000000..44972cad21 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json @@ -0,0 +1,188 @@ +{ + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "label", + "label": "Collectors" + }, + { + "type": "dict", + "collapsable": true, + "key": "CollectMayaRender", + "label": "Collect Render Layers", + "children": [ + { + "type": "boolean", + "key": "sync_workfile_version", + "label": "Sync render version with workfile" + }] + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Collectors" + }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateModelName", + "label": "Validate Model Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" + }, + { + "type": "path-widget", + "key": "material_file", + "label": "Material File", + "multiplatform": true, + "multipath": false + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateAssemblyName", + "label": "Validate Assembly Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateShaderName", + "label": "ValidateShaderName", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" + + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateMeshHasOverlappingUVs", + "label": "ValidateMeshHasOverlappingUVs", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Extractors" + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractCameraAlembic", + "label": "Extract camera to Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "List of attributes that will be added to the baked alembic camera. Needs to be written in python list syntax.

For example:
[\"attributeName\", \"anotherAttribute\"]

" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "raw-json", + "key": "bake_attributes", + "label": "Bake Attributes" + + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "MayaSubmitDeadline", + "label": "Submit maya job to deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "enum", + "key": "tile_assembler_plugin", + "label": "Tile Assembler Plugin", + "multiselection": false, + "enum_items": [ + { + "DraftTileAssembler": "Draft Tile Assembler" + }, + { + "oiio": "Open Image IO" + }] + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_color.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_color.json new file mode 100644 index 0000000000..ac4313490b --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_color.json @@ -0,0 +1,28 @@ +[ + { + "type": "list-strict", + "key": "{name}", + "label": "{label}:", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + } + ] + } +] diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json new file mode 100644 index 0000000000..eaeccf2c87 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json @@ -0,0 +1,43 @@ +[ + { + "type": "dict", + "collapsable": true, + "key": "create_{name}", + "label": "{label}", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "label", + "label": "Label" + }, + { + "type": "text", + "key": "family", + "label": "Family" + }, + { + "type": "text", + "key": "icon", + "label": "Icon" + }, + { + "type": "list", + "key": "defaults", + "label": "Defaults", + "object_type": { + "type": "text" + } + }, + { + "type": "text", + "key": "help", + "label": "Help" + } + ] + } +] From b304eef0f58cf727ff28bee678195a59564ec501 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Nov 2020 15:07:27 +0100 Subject: [PATCH 063/219] feat(imageio): final schema and default setting for hiero and nuke --- .../defaults/project_anatomy/imageio.json | 161 +++-- .../schema_anatomy_imageio.json | 568 +++++++++--------- 2 files changed, 418 insertions(+), 311 deletions(-) diff --git a/pype/settings/defaults/project_anatomy/imageio.json b/pype/settings/defaults/project_anatomy/imageio.json index 8b934f810d..1adab0fc2b 100644 --- a/pype/settings/defaults/project_anatomy/imageio.json +++ b/pype/settings/defaults/project_anatomy/imageio.json @@ -1,42 +1,131 @@ { - "nuke": { - "root": { - "colorManagement": "Nuke", - "OCIO_config": "nuke-default", - "defaultViewerLUT": "Nuke Root LUTs", - "monitorLut": "sRGB", - "int8Lut": "sRGB", - "int16Lut": "sRGB", - "logLut": "Cineon", - "floatLut": "linear" - }, - "viewer": { - "viewerProcess": "sRGB" - }, - "write": { - "render": { - "colorspace": "linear" + "hosts": { + "hiero": { + "workfile": { + "ocioConfigName": "nuke-default", + "ocioconfigpath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpace": "linear", + "sixteenBitLut": "sRGB", + "eightBitLut": "sRGB", + "floatLut": "linear", + "logLut": "Cineon", + "viewerLut": "sRGB", + "thumbnailLut": "sRGB" }, - "prerender": { - "colorspace": "linear" - }, - "still": { - "colorspace": "sRGB" + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", + "colorspace": "sRGB" + } + ] } }, - "read": { - "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]": "linear", - "[^-a-zA-Z0-9](P|N|Z|crypto)[^-a-zA-Z0-9]": "linear", - "[^-a-zA-Z0-9](plateRef)[^-a-zA-Z0-9]": "sRGB" + "nuke": { + "workfile": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "customOCIOConfigPath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpaceLUT": "linear", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "nodes": { + "requiredNodes": [ + { + "plugins": [ + "CreateWriteRender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "True" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + }, + { + "plugins": [ + "CreateWritePrerender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "False" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + } + ], + "customNodes": [] + }, + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]", + "colorspace": "linear" + } + ] + } } - }, - "maya": { - - }, - "houdini": { - - }, - "resolve": { - } -} +} \ No newline at end of file diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json index d1a286b883..b7de6c5091 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json @@ -1,280 +1,298 @@ { + "type": "dict", + "key": "imageio", + "label": "Color Management (Image i/o)", + "is_file": true, + "children": [{ + "key": "hosts", "type": "dict", - "key": "imageio", - "label": "Color Management (Image i/o)", - "is_file": true, - "children": [ - { - "key": "hosts", - "type": "dict", - "label": "Hosts", - "collapsed": false, - "collapsable": true, - "children": [ + "label": "Hosts", + "collapsed": false, + "collapsable": true, + "children": [{ + "key": "hiero", + "type": "dict", + "label": "Hiero", + "children": [{ + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsable": false, + "children": [{ + "type": "form", + "children": [{ + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [{ + "nuke-default": "nuke-default" + }, { - "key": "hiero", - "type": "dict", - "label": "Hiero", - "children": [ - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsable": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "ocioConfigName", - "label": "OpenColorIO Config", - "enum_items": [ - {"nuke-default": "nuke-default"}, - {"aces_1.0.3": "aces_1.0.3"}, - {"aces_1.1": "aces_1.1"}, - {"custom": "custom"} - ] - }, { - "type": "path-widget", - "key": "ocioconfigpath", - "label": "Custom OCIO path", - "multiplatform": true, - "multipath": true - }, { - "type": "text", - "key": "workingSpace", - "label": "Working Space" - }, { - "type": "text", - "key": "sixteenBitLut", - "label": "16 Bit Files" - }, { - "type": "text", - "key": "eightBitLut", - "label": "8 Bit Files" - }, { - "type": "text", - "key": "floatLut", - "label": "Floating Point Files" - }, { - "type": "text", - "key": "logLut", - "label": "Log Files" - }, { - "type": "text", - "key": "viewerLut", - "label": "Viewer" - }, { - "type": "text", - "key": "thumbnailLut", - "label": "Thumbnails" - } - ] - } - ] - }, { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsable": false, - "children": [ - { - "type": "list", - "key": "inputs", - "label": "", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] - }, { - "key": "nuke", - "type": "dict", - "label": "Nuke", - "children": [ - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsable": false, - "is_group": true, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "colorManagement", - "label": "color management", - "enum_items": [ - {"Nuke": "Nuke"}, - {"OCIO": "OCIO"} - ] - }, { - "type": "enum", - "key": "OCIO_config", - "label": "OpenColorIO Config", - "enum_items": [ - {"nuke-default": "nuke-default"}, - {"spi-vfx": "spi-vfx"}, - {"spi-anim": "spi-anim"}, - {"aces_1.0.3": "aces_0.1.1"}, - {"aces_1.0.3": "aces_0.7.1"}, - {"aces_1.0.3": "aces_1.0.1"}, - {"aces_1.0.3": "aces_1.0.3"}, - {"aces_1.1": "aces_1.1"}, - {"custom": "custom"} - ] - }, { - "type": "path-widget", - "key": "customOCIOConfigPath", - "label": "Custom OCIO config path", - "multiplatform": true, - "multipath": true - }, { - "type": "text", - "key": "workingSpaceLUT", - "label": "Working Space" - }, { - "type": "text", - "key": "monitorLut", - "label": "monitor" - }, { - "type": "text", - "key": "int8Lut", - "label": "8-bit files" - }, { - "type": "text", - "key": "int16Lut", - "label": "16-bit files" - }, { - "type": "text", - "key": "logLut", - "label": "log files" - }, { - "type": "text", - "key": "floatLut", - "label": "float files" - } - ] - } - ] - }, { - "key": "nodes", - "type": "dict", - "label": "Nodes", - "collapsable": false, - "is_group": true, - "children": [ - { - "key": "CreateWriteRender", - "label": "CreateWriteRender", - "type": "dict", - "children": [ - { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, { - "type": "splitter" - }, - { - "key": "knobs", - "label": "Knobs", - "type": "list", - "object_type": { - "type": "dict-item", - "children": [ - { - "type": "text", - "key": "name", - "label": "Name" - }, { - "type": "text", - "key": "value", - "label": "Value" - } - ] - } - } - ] - }, { - "type": "list", - "key": "custom-items", - "label": "Custom Nodes", - "object_type": { - "type": "dict", - "children": [ - { - "key": "nodeCreateFamily", - "label": "Creator Family", - "type": "text" - }, { - "type": "text", - "key": "nodeClass", - "label": "Node Class" - }, { - "type": "splitter" - }, - { - "type": "dict-modifiable", - "key": "nodeKnobs", - "label": "Node Knobs", - "highlight_content": true, - "object_type": { - "type": "text", - "key": "nodeKnobValue" - } - } - ] - } - } - ] - }, { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsable": false, - "children": [ - { - "type": "list", - "key": "inputs", - "label": "", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" } - - ] - } - ] + ] + }, { + "type": "path-widget", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" + }] + }] + }, { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsable": true, + "children": [{ + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "regex", + "label": "Regex" + }, { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + }] + } + }] + }] + }, { + "key": "nuke", + "type": "dict", + "label": "Nuke", + "children": [{ + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsable": false, + "is_group": true, + "children": [{ + "type": "form", + "children": [{ + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [{ + "Nuke": "Nuke" + }, + { + "OCIO": "OCIO" + } + ] + }, { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [{ + "nuke-default": "nuke-default" + }, + { + "spi-vfx": "spi-vfx" + }, + { + "spi-anim": "spi-anim" + }, + { + "aces_1.0.3": "aces_0.1.1" + }, + { + "aces_1.0.3": "aces_0.7.1" + }, + { + "aces_1.0.3": "aces_1.0.1" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, { + "type": "path-widget", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, { + "type": "text", + "key": "logLut", + "label": "log files" + }, { + "type": "text", + "key": "floatLut", + "label": "float files" + }] + }] + }, { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsable": true, + "is_group": true, + "children": [ + { + "key": "requiredNodes", + "type": "list", + "label": "Required Nodes", + "object_type": { + "type": "dict", + "children": [{ + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass", + "label": "Plugin Class" + } + }, { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, { + "type": "splitter" + }, { + "key": "knobs", + "label": "Knobs", + "type": "list", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "name", + "label": "Name" + }, { + "type": "text", + "key": "value", + "label": "Value" + }] + } + }] + } + }, { + "type": "list", + "key": "customNodes", + "label": "Custom Nodes", + "object_type": { + "type": "dict", + "children": [{ + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass", + "label": "Plugin Class" + } + }, { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, { + "type": "splitter" + }, { + "key": "knobs", + "label": "Knobs", + "type": "list", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "name", + "label": "Name" + }, { + "type": "text", + "key": "value", + "label": "Value" + }] + } + }] + } + } + ] + }, { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsable": true, + "children": [{ + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "regex", + "label": "Regex" + }, { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + }] + } + }] + }] + }] + }] } From 038945374962b6e08bbb9315a39aa66bd6673dcd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Nov 2020 15:10:48 +0100 Subject: [PATCH 064/219] fix(settings): removing examples --- .../settings/gui_schemas/system_schema/schema_main.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json index 3ec652c302..8e8798149c 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json @@ -17,9 +17,6 @@ }, { "type": "schema", "name": "schema_tools" - }, { - "type": "schema", - "name": "example_schema" }] } ] From 1de4100adb17f7b5d51a309432264c6d4b50fdbe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Nov 2020 18:06:29 +0100 Subject: [PATCH 065/219] modifiable dictionary can use schemas and templates in --- pype/tools/settings/settings/widgets/lib.py | 87 ++++++++++++--------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 1ec46f92b9..f112a6e975 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -213,45 +213,62 @@ def _fill_inner_schemas(schema_data, schema_collection, schema_templates): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") - children = schema_data.get("children") - if not children: - return schema_data - - new_children = [] - for child in children: - child_type = child["type"] - if child_type == "schema": - schema_name = child["name"] - if schema_name not in schema_collection: - if schema_name in schema_templates: - raise KeyError(( - "Schema template \"{}\" is used as `schema`" - ).format(schema_name)) - raise KeyError( - "Schema \"{}\" was not found".format(schema_name) - ) - - filled_child = _fill_inner_schemas( - schema_collection[schema_name], - schema_collection, - schema_templates - ) - - elif child_type == "schema_template": - for filled_child in _fill_schema_template( - child, schema_collection, schema_templates - ): - new_children.append(filled_child) + children_key = "children" + object_type_key = "object_type" + for item_key in (children_key, object_type_key): + children = schema_data.get(item_key) + if not children: continue - else: - filled_child = _fill_inner_schemas( - child, schema_collection, schema_templates - ) + if object_type_key == item_key: + if not isinstance(children, dict): + continue + children = [children] - new_children.append(filled_child) + new_children = [] + for child in children: + child_type = child["type"] + if child_type == "schema": + schema_name = child["name"] + if schema_name not in schema_collection: + if schema_name in schema_templates: + raise KeyError(( + "Schema template \"{}\" is used as `schema`" + ).format(schema_name)) + raise KeyError( + "Schema \"{}\" was not found".format(schema_name) + ) - schema_data["children"] = new_children + filled_child = _fill_inner_schemas( + schema_collection[schema_name], + schema_collection, + schema_templates + ) + + elif child_type == "schema_template": + for filled_child in _fill_schema_template( + child, schema_collection, schema_templates + ): + new_children.append(filled_child) + continue + + else: + filled_child = _fill_inner_schemas( + child, schema_collection, schema_templates + ) + + new_children.append(filled_child) + + if item_key == object_type_key: + if len(new_children) != 1: + raise KeyError(( + "Failed to fill object type with type: {} | name {}" + ).format( + child_type, str(child.get("name")) + )) + new_children = new_children[0] + + schema_data[item_key] = new_children return schema_data From f0b50b9491ecb988778a6c272ddafc2fa7e639b8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Nov 2020 18:07:35 +0100 Subject: [PATCH 066/219] added also separator to replace splitter --- pype/tools/settings/settings/widgets/item_types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index be508d6617..02cd34416a 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3771,6 +3771,7 @@ TypeToKlass.types["list-strict"] = ListStrictWidget TypeToKlass.types["enum"] = EnumeratorWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict # DEPRECATED - remove when removed from schemas +TypeToKlass.types["splitter"] = SplitterWidget TypeToKlass.types["dict-item"] = DictWidget TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-invisible"] = DictInvisible @@ -3778,4 +3779,4 @@ TypeToKlass.types["path-widget"] = PathWidget TypeToKlass.types["form"] = DictFormWidget TypeToKlass.types["label"] = LabelWidget -TypeToKlass.types["splitter"] = SplitterWidget +TypeToKlass.types["separator"] = SplitterWidget From 0370600f86d00c4e3394da794003d1e294ce5970 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 18:19:44 +0100 Subject: [PATCH 067/219] add burnin and families SP --- .../defaults/project_settings/global.json | 9 +- .../project_settings/standalonepublisher.json | 12 -- .../schema_project_global.json | 179 ++++++++++++------ .../schema_project_standalonepublisher.json | 150 ++++----------- .../schemas/template_creatorfamily.json | 43 ----- 5 files changed, 167 insertions(+), 226 deletions(-) delete mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index b68f6d87bd..dd379b6180 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -57,17 +57,18 @@ "y_offset": 5, "bg_padding": 5 }, - "fields": {}, "profiles": [ { + "families": [], + "hosts": [], "burnins": { "burnin": { "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_RIGHT": "{anatomy[version]}", "TOP_CENTERED": "", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "TOP_RIGHT": "{anatomy[version]}", + "BOTTOM_LEFT": "{username}", "BOTTOM_CENTERED": "{asset}", - "BOTTOM_LEFT": "{username}" + "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}" } } } diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json index 21ea3db586..85b7a55b9c 100644 --- a/pype/settings/defaults/project_settings/standalonepublisher.json +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -97,18 +97,6 @@ "icon": "", "defaults": [], "help": "" - }, - "other_families": { - "create_background": { - "name": "background", - "label": "Background", - "family": "background", - "icon": "image", - "defaults": [ - "Main" - ], - "help": "Layered background from PSD." - } } } } \ No newline at end of file diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json index ef8fc58257..a4c98ecddb 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json @@ -195,58 +195,120 @@ "checkbox_key": "enabled", "is_group": true, "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "dict", - "collapsable": true, - "key": "options", - "label": "Burnin formating options", - "children": [ { - "type": "number", - "key": "font_size", - "label": "Font size" + "type": "boolean", + "key": "enabled", + "label": "Enabled" }, { - "type": "number", - "key": "opacity", - "label": "Font opacity" + "type": "dict", + "collapsable": true, + "key": "options", + "label": "Burnin formating options", + "children": [ + { + "type": "number", + "key": "font_size", + "label": "Font size" + }, + { + "type": "number", + "key": "opacity", + "label": "Font opacity" + }, + { + "type": "number", + "key": "bg_opacity", + "label": "Background opacity" + }, + { + "type": "number", + "key": "x_offset", + "label": "X Offset" + }, + { + "type": "number", + "key": "y_offset", + "label": "Y Offset" + }, + { + "type": "number", + "key": "bg_padding", + "label": "Padding aroung text" + }, + { + "type": "splitter" + }] }, + { - "type": "number", - "key": "bg_opacity", - "label": "Background opacity" - }, - { - "type": "number", - "key": "x_offset", - "label": "X Offset" - }, - { - "type": "number", - "key": "y_offset", - "label": "Y Offset" - }, - { - "type": "number", - "key": "bg_padding", - "label": "Padding aroung text" - }] - }, - { - "type": "raw-json", - "key": "fields", - "label": "Burnin Fields" - }, - { - "type": "raw-json", - "key": "profiles", - "label": "Burnin profiles" - }] + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "burnins", + "label": "Burnins", + "type": "dict-modifiable", + "highlight_content": true, + "collapsable": false, + "object_type": + { + "type": "dict", + "children": [ + { + "key": "TOP_LEFT", + "label": "Top Left", + "type": "text" + }, + { + "key": "TOP_CENTERED", + "label": "Top Centered", + "type": "text" + }, + { + "key": "TOP_RIGHT", + "label": "top Right", + "type": "text" + }, + { + "key": "BOTTOM_LEFT", + "label": "Bottom Left", + "type": "text" + }, + { + "key": "BOTTOM_CENTERED", + "label": "Bottom Centered", + "type": "text" + }, + { + "key": "BOTTOM_RIGHT", + "label": "BottomRight", + "type": "text" + }] + } + }] + } + } + ] }, { "type": "dict", @@ -308,35 +370,38 @@ "type": "list", "key": "maya", "label": "Maya", - "object_type":{ - "type":"text" + "object_type": + { + "type": "text" } }, { "type": "list", "key": "nuke", "label": "Nuke", - "object_type":{ - "type":"text" + "object_type": + { + "type": "text" } }, { "type": "list", "key": "aftereffects", "label": "After Effects", - "object_type":{ - "type":"text" + "object_type": + { + "type": "text" } }, { "type": "list", "key": "celaction", "label": "Celaction", - "object_type":{ - "type":"text" + "object_type": + { + "type": "text" } - } - ] + }] }] }] }] diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json index 5e07d82de6..4a6e5f76ec 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json @@ -41,119 +41,49 @@ }] }, { - "type": "dict", + "type": "dict-modifiable", "collapsable": true, "key": "create", "label": "Creator plugins", "is_file": true, - "children": [ - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Workfile", - "name": "workfile" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Model", - "name": "model" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Look", - "name": "look" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Rig", - "name": "rig" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Pointcache", - "name": "pointcache" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Plate", - "name": "plate" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Camera", - "name": "camera" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Maya Scene", - "name": "mayaAscii" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Editorial", - "name": "editorial" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Image", - "name": "image" - }] - }, - { - "type": "schema_template", - "name": "template_creatorfamily", - "template_data": [ - { - "label": "Matchmove", - "name": "matchmove" - }] - }, - { - "type": "dict-modifiable", - "key": "other_families", - "label": "Other Families", - "object_type": { - "type": "raw-json" - } - }] + "object_type": + { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "label", + "label": "Label" + }, + { + "type": "text", + "key": "family", + "label": "Family" + }, + { + "type": "text", + "key": "icon", + "label": "Icon" + }, + { + "type": "list", + "key": "defaults", + "label": "Defaults", + "object_type": + { + "type": "text" + } + }, + { + "type": "text", + "key": "help", + "label": "Help" + }] + } }] - } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json deleted file mode 100644 index eaeccf2c87..0000000000 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/template_creatorfamily.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "type": "dict", - "collapsable": true, - "key": "create_{name}", - "label": "{label}", - "children": [ - { - "type": "text", - "key": "name", - "label": "Name" - }, - { - "type": "text", - "key": "label", - "label": "Label" - }, - { - "type": "text", - "key": "family", - "label": "Family" - }, - { - "type": "text", - "key": "icon", - "label": "Icon" - }, - { - "type": "list", - "key": "defaults", - "label": "Defaults", - "object_type": { - "type": "text" - } - }, - { - "type": "text", - "key": "help", - "label": "Help" - } - ] - } -] From 34b568304eae58866ff7ffdf1107f9818f95d046 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Nov 2020 18:20:26 +0100 Subject: [PATCH 068/219] fix(hiero): trimming review with clip event number --- pype/plugins/hiero/publish/extract_review_cutup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/hiero/publish/extract_review_cutup.py b/pype/plugins/hiero/publish/extract_review_cutup.py index ace6bc88d3..210def3448 100644 --- a/pype/plugins/hiero/publish/extract_review_cutup.py +++ b/pype/plugins/hiero/publish/extract_review_cutup.py @@ -23,6 +23,8 @@ class ExtractReviewCutUp(pype.api.Extractor): def process(self, instance): inst_data = instance.data asset = inst_data['asset'] + item = inst_data['item'] + event_number = int(item.eventNumber()) # get representation and loop them representations = inst_data["representations"] @@ -97,7 +99,7 @@ class ExtractReviewCutUp(pype.api.Extractor): index = 0 for image in collection: dst_file_num = frame_start + index - dst_file_name = head + str(padding % dst_file_num) + tail + dst_file_name = str(event_number) + head + str(padding % dst_file_num) + tail src = os.path.join(staging_dir, image) dst = os.path.join(full_output_dir, dst_file_name) self.log.info("Creating temp hardlinks: {}".format(dst)) From 61e703f13a7c9e65e9d7bdb911da70b95524e411 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Nov 2020 18:23:08 +0100 Subject: [PATCH 069/219] hound(hiero): improving variable building --- pype/plugins/hiero/publish/extract_review_cutup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/plugins/hiero/publish/extract_review_cutup.py b/pype/plugins/hiero/publish/extract_review_cutup.py index 210def3448..87e584d0b0 100644 --- a/pype/plugins/hiero/publish/extract_review_cutup.py +++ b/pype/plugins/hiero/publish/extract_review_cutup.py @@ -99,7 +99,12 @@ class ExtractReviewCutUp(pype.api.Extractor): index = 0 for image in collection: dst_file_num = frame_start + index - dst_file_name = str(event_number) + head + str(padding % dst_file_num) + tail + dst_file_name = "".join([ + str(event_number), + head, + str(padding % dst_file_num), + tail + ]) src = os.path.join(staging_dir, image) dst = os.path.join(full_output_dir, dst_file_name) self.log.info("Creating temp hardlinks: {}".format(dst)) From aff450992a9547732c429d68efc22e9ad1b0e276 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 18:31:58 +0100 Subject: [PATCH 070/219] remove old dict-invisible --- .../gui_schemas/projects_schema/schema_project_global.json | 2 +- .../system_schema/module_settings/schema_ftrack.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json index a4c98ecddb..6fdc6a23a5 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json @@ -40,7 +40,7 @@ "label": "Enabled" }, { - "type": "dict-invisible", + "type": "dict", "key": "ffmpeg_args", "children": [ { diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 9a0d36ad06..8dd219e98e 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -142,7 +142,7 @@ }, { "key": "intent", - "type": "dict-invisible", + "type": "dict", "children": [{ "type": "label", "label": "Intent" From 327ff56ded1e8b47c5e6c4d6828f884d804d95d9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 26 Nov 2020 19:23:01 +0100 Subject: [PATCH 071/219] filled publish filter settings --- .../defaults/project_settings/hiero.json | 15 +- .../defaults/project_settings/maya.json | 10 +- .../defaults/project_settings/nuke.json | 2 +- .../project_settings/standalonepublisher.json | 152 ++++++++++-------- .../{global => }/applications.json | 0 .../system_settings/{global => }/general.json | 0 .../system_settings/{global => }/hosts.json | 0 .../system_settings/{global => }/intent.json | 0 .../system_settings/{global => }/modules.json | 0 .../standalone_publish/families.json | 90 ----------- .../system_settings/{global => }/tools.json | 0 .../projects_schema/schema_project_hiero.json | 5 +- .../projects_schema/schema_project_maya.json | 5 +- .../projects_schema/schema_project_nuke.json | 5 +- .../schemas/schema_publish_gui_filter.json | 12 ++ .../system_schema/schema_main.json | 32 ++-- 16 files changed, 127 insertions(+), 201 deletions(-) rename pype/settings/defaults/system_settings/{global => }/applications.json (100%) rename pype/settings/defaults/system_settings/{global => }/general.json (100%) rename pype/settings/defaults/system_settings/{global => }/hosts.json (100%) rename pype/settings/defaults/system_settings/{global => }/intent.json (100%) rename pype/settings/defaults/system_settings/{global => }/modules.json (100%) delete mode 100644 pype/settings/defaults/system_settings/standalone_publish/families.json rename pype/settings/defaults/system_settings/{global => }/tools.json (100%) create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json diff --git a/pype/settings/defaults/project_settings/hiero.json b/pype/settings/defaults/project_settings/hiero.json index e7e6350c29..e4e65eedd3 100644 --- a/pype/settings/defaults/project_settings/hiero.json +++ b/pype/settings/defaults/project_settings/hiero.json @@ -5,17 +5,10 @@ }, "ExtractReviewCutUpVideo": { "enabled": true, - "tags_addition": [] + "tags_addition": [ + "review" + ] } }, - "filter": { - "strict": { - "ValidateVersion": true, - "VersionUpWorkfile": true - }, - "benevolent": { - "ValidateVersion": false, - "VersionUpWorkfile": false - } - } + "filters": {} } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index f8315dc129..afc4442d0f 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -307,5 +307,13 @@ } ] }, - "filter": {} + "filters": { + "preset 1": { + "ValidateNoAnimation": false, + "ValidateShapeDefaultNames": false + }, + "preset 2": { + "ValidateNoAnimation": false + } + } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json index 5b9ef5c21c..873f249769 100644 --- a/pype/settings/defaults/project_settings/nuke.json +++ b/pype/settings/defaults/project_settings/nuke.json @@ -86,5 +86,5 @@ } ] }, - "filter": {} + "filters": {} } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json index 85b7a55b9c..2b5db54a4f 100644 --- a/pype/settings/defaults/project_settings/standalonepublisher.json +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -11,92 +11,104 @@ }, "create": { "create_workfile": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "workfile", + "label": "Workfile", + "family": "workfile", + "icon": "cube", + "defaults": [ + "Main" + ], + "help": "Working scene backup" }, "create_model": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" - }, - "create_look": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "mode", + "label": "Model", + "family": "model", + "icon": "cube", + "defaults": [ + "Main" + ], + "help": "Polygonal static geometry" }, "create_rig": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "rig", + "label": "Rig", + "family": "rig", + "icon": "wheelchair", + "defaults": [ + "Main", + "Cloth" + ], + "help": "Artist-friendly rig with controls" }, "create_pointcache": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "pointcache", + "label": "Pointcache", + "family": "pointcache", + "icon": "gears", + "defaults": [ + "Main" + ], + "help": "Alembic pointcache for animated data" }, "create_plate": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "plate", + "label": "Plate", + "family": "plate", + "icon": "camera", + "defaults": [ + "Main", + "BG", + "Animatic", + "Reference", + "Offline" + ], + "help": "Footage for composting or reference" }, "create_camera": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" - }, - "create_mayaAscii": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "camera", + "label": "Camera", + "family": "camera", + "icon": "camera", + "defaults": [ + "Main" + ], + "help": "video-camera" }, "create_editorial": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "editorial", + "label": "Editorial", + "family": "editorial", + "icon": "image", + "defaults": [ + "Main" + ], + "help": "Editorial files to generate shots." }, "create_image": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "image", + "label": "Image file", + "family": "image", + "icon": "image", + "defaults": [ + "Reference", + "Texture", + "ConceptArt", + "MattePaint" + ], + "help": "Holder for all kinds of image data" }, "create_matchmove": { - "name": "", - "label": "", - "family": "", - "icon": "", - "defaults": [], - "help": "" + "name": "matchmove", + "label": "Matchmove script", + "family": "matchmove", + "icon": "empire", + "defaults": [ + "Camera", + "Object", + "Mocap" + ], + "help": "Script exported from matchmoving application" } } } \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/applications.json similarity index 100% rename from pype/settings/defaults/system_settings/global/applications.json rename to pype/settings/defaults/system_settings/applications.json diff --git a/pype/settings/defaults/system_settings/global/general.json b/pype/settings/defaults/system_settings/general.json similarity index 100% rename from pype/settings/defaults/system_settings/global/general.json rename to pype/settings/defaults/system_settings/general.json diff --git a/pype/settings/defaults/system_settings/global/hosts.json b/pype/settings/defaults/system_settings/hosts.json similarity index 100% rename from pype/settings/defaults/system_settings/global/hosts.json rename to pype/settings/defaults/system_settings/hosts.json diff --git a/pype/settings/defaults/system_settings/global/intent.json b/pype/settings/defaults/system_settings/intent.json similarity index 100% rename from pype/settings/defaults/system_settings/global/intent.json rename to pype/settings/defaults/system_settings/intent.json diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/modules.json similarity index 100% rename from pype/settings/defaults/system_settings/global/modules.json rename to pype/settings/defaults/system_settings/modules.json diff --git a/pype/settings/defaults/system_settings/standalone_publish/families.json b/pype/settings/defaults/system_settings/standalone_publish/families.json deleted file mode 100644 index d05941cc26..0000000000 --- a/pype/settings/defaults/system_settings/standalone_publish/families.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "create_look": { - "name": "look", - "label": "Look", - "family": "look", - "icon": "paint-brush", - "defaults": ["Main"], - "help": "Shader connections defining shape look" - }, - "create_model": { - "name": "model", - "label": "Model", - "family": "model", - "icon": "cube", - "defaults": ["Main", "Proxy", "Sculpt"], - "help": "Polygonal static geometry" - }, - "create_workfile": { - "name": "workfile", - "label": "Workfile", - "family": "workfile", - "icon": "cube", - "defaults": ["Main"], - "help": "Working scene backup" - }, - "create_camera": { - "name": "camera", - "label": "Camera", - "family": "camera", - "icon": "video-camera", - "defaults": ["Main"], - "help": "Single baked camera" - }, - "create_pointcache": { - "name": "pointcache", - "label": "Pointcache", - "family": "pointcache", - "icon": "gears", - "defaults": ["Main"], - "help": "Alembic pointcache for animated data" - }, - "create_rig": { - "name": "rig", - "label": "Rig", - "family": "rig", - "icon": "wheelchair", - "defaults": ["Main"], - "help": "Artist-friendly rig with controls" - }, - "create_layout": { - "name": "layout", - "label": "Layout", - "family": "layout", - "icon": "cubes", - "defaults": ["Main"], - "help": "Simple scene for animators with camera" - }, - "create_plate": { - "name": "plate", - "label": "Plate", - "family": "plate", - "icon": "camera", - "defaults": ["Main", "BG", "Reference"], - "help": "Plates for compositors" - }, - "create_matchmove": { - "name": "matchmove", - "label": "Matchmove script", - "family": "matchmove", - "icon": "empire", - "defaults": ["Camera", "Object", "Mocap"], - "help": "Script exported from matchmoving application" - }, - "create_images": { - "name": "image", - "label": "Image file", - "family": "image", - "icon": "image", - "defaults": ["ConceptArt", "Reference", "Texture", "MattePaint"], - "help": "Holder for all kinds of image data" - }, - "create_editorial": { - "name": "editorial", - "label": "Editorial", - "family": "editorial", - "icon": "image", - "defaults": ["Main"], - "help": "Editorial files to generate shots." - } -} diff --git a/pype/settings/defaults/system_settings/global/tools.json b/pype/settings/defaults/system_settings/tools.json similarity index 100% rename from pype/settings/defaults/system_settings/global/tools.json rename to pype/settings/defaults/system_settings/tools.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json index 0f8475655c..834dc0bb3d 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_hiero.json @@ -41,8 +41,7 @@ }] }, { - "type": "raw-json", - "key": "filter", - "label": "Publish GUI Filters" + "type": "schema", + "name": "schema_publish_gui_filter" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json index d863941d1d..a71cfb1e09 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_maya.json @@ -21,8 +21,7 @@ "name": "schema_workfile_build" }, { - "type": "raw-json", - "key": "filter", - "label": "Publish GUI Filters" + "type": "schema", + "name": "schema_publish_gui_filter" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json index 48d78cc422..3870fbe8bd 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json @@ -138,8 +138,7 @@ "name": "schema_workfile_build" }, { - "type": "raw-json", - "key": "filter", - "label": "Publish GUI Filters" + "type": "schema", + "name": "schema_publish_gui_filter" }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json new file mode 100644 index 0000000000..f2385996eb --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json @@ -0,0 +1,12 @@ +{ + "type": "dict-modifiable", + "collapsable": true, + "key": "filters", + "label": "Publish GUI Filters", + "is_file": true, + "object_type": + { + "type": "raw-json", + "label": "Plugins" + } +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json index 039d00401c..d5c7402dcf 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json @@ -1,23 +1,17 @@ { "key": "system", "type": "dict", - "children": [ - { - "type": "dict", - "key": "global", - "children": [{ - "type": "schema", - "name": "schema_general" - },{ - "type": "schema", - "name": "schema_modules" - }, { - "type": "schema", - "name": "schema_applications" - }, { - "type": "schema", - "name": "schema_tools" - }] - } - ] + "children": [{ + "type": "schema", + "name": "schema_general" + },{ + "type": "schema", + "name": "schema_modules" + }, { + "type": "schema", + "name": "schema_applications" + }, { + "type": "schema", + "name": "schema_tools" + }] } From 5fc00fa6f48b407e8a528abfb15637ff51b56a62 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:15:19 +0200 Subject: [PATCH 072/219] renamed `load_json` to `load_json_file` --- pype/settings/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index b40e726ad1..e316b1697b 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -77,7 +77,7 @@ def default_settings(): return copy.deepcopy(_DEFAULT_SETTINGS) -def load_json(fpath): +def load_json_file(fpath): # Load json data with open(fpath, "r") as opened_file: lines = opened_file.read().splitlines() From effa7a28f8dedbe38ffb4c63a3bc72c53a2a246a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:15:52 +0200 Subject: [PATCH 073/219] simplified `load_json_file` as jsons are expected to be saved with gui --- pype/settings/lib.py | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index e316b1697b..953cbda0e6 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -79,48 +79,9 @@ def default_settings(): def load_json_file(fpath): # Load json data - with open(fpath, "r") as opened_file: - lines = opened_file.read().splitlines() - - # prepare json string - standard_json = "" - for line in lines: - # Remove all whitespace on both sides - line = line.strip() - - # Skip blank lines - if len(line) == 0: - continue - - standard_json += line - - # Check if has extra commas - extra_comma = False - if ",]" in standard_json or ",}" in standard_json: - extra_comma = True - standard_json = standard_json.replace(",]", "]") - standard_json = standard_json.replace(",}", "}") - - if extra_comma: - log.error("Extra comma in json file: \"{}\"".format(fpath)) - - # return empty dict if file is empty - if standard_json == "": - return {} - - # Try to parse string - try: - return json.loads(standard_json) - - except json.decoder.JSONDecodeError: - # Return empty dict if it is first time that decode error happened - return {} - - # Repreduce the exact same exception but traceback contains better - # information about position of error in the loaded json try: with open(fpath, "r") as opened_file: - json.load(opened_file) + return json.load(opened_file) except json.decoder.JSONDecodeError: log.warning( From d5b55f60b04eb2df6fa47afc1b065a9da27ba399 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:16:41 +0200 Subject: [PATCH 074/219] changed `load_json` to `load_json_file` in code --- pype/settings/lib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 953cbda0e6..982ae07490 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -199,25 +199,25 @@ def load_jsons_from_dir(path, *args, **kwargs): def studio_system_settings(): if os.path.exists(SYSTEM_SETTINGS_PATH): - return load_json(SYSTEM_SETTINGS_PATH) + return load_json_file(SYSTEM_SETTINGS_PATH) return {} def studio_environments(): if os.path.exists(ENVIRONMENTS_PATH): - return load_json(ENVIRONMENTS_PATH) + return load_json_file(ENVIRONMENTS_PATH) return {} def studio_project_settings(): if os.path.exists(PROJECT_SETTINGS_PATH): - return load_json(PROJECT_SETTINGS_PATH) + return load_json_file(PROJECT_SETTINGS_PATH) return {} def studio_project_anatomy(): if os.path.exists(PROJECT_ANATOMY_PATH): - return load_json(PROJECT_ANATOMY_PATH) + return load_json_file(PROJECT_ANATOMY_PATH) return {} @@ -244,7 +244,7 @@ def project_settings_overrides(project_name): path_to_json = path_to_project_overrides(project_name) if not os.path.exists(path_to_json): return {} - return load_json(path_to_json) + return load_json_file(path_to_json) def project_anatomy_overrides(project_name): @@ -254,7 +254,7 @@ def project_anatomy_overrides(project_name): path_to_json = path_to_project_anatomy(project_name) if not os.path.exists(path_to_json): return {} - return load_json(path_to_json) + return load_json_file(path_to_json) def merge_overrides(global_dict, override_dict): From 8d23f491d2f52f7cd4f3a5fb964ac404f85ce72d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:17:08 +0200 Subject: [PATCH 075/219] moved `load_jsons_from_dir` # Conflicts: # pype/settings/lib.py --- pype/settings/lib.py | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 982ae07490..de1aed31f6 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -92,7 +92,45 @@ def load_json_file(fpath): return {} -def find_environments(data, with_items=False, parents=None): +def load_jsons_from_dir(path, *args, **kwargs): + output = {} + + path = os.path.normpath(path) + if not os.path.exists(path): + # TODO warning + return output + + sub_keys = list(kwargs.pop("subkeys", args)) + for sub_key in tuple(sub_keys): + _path = os.path.join(path, sub_key) + if not os.path.exists(_path): + break + + path = _path + sub_keys.pop(0) + + base_len = len(path) + 1 + for base, _directories, filenames in os.walk(path): + base_items_str = base[base_len:] + if not base_items_str: + base_items = [] + else: + base_items = base_items_str.split(os.path.sep) + + for filename in filenames: + basename, ext = os.path.splitext(filename) + if ext == ".json": + full_path = os.path.join(base, filename) + value = load_json_file(full_path) + dict_keys = base_items + [basename] + output = subkey_merge(output, value, dict_keys) + + for sub_key in sub_keys: + output = output[sub_key] + return output + + +def find_environments(data): if not data or not isinstance(data, dict): return {} @@ -159,44 +197,6 @@ def subkey_merge(_dict, value, keys): return _dict -def load_jsons_from_dir(path, *args, **kwargs): - output = {} - - path = os.path.normpath(path) - if not os.path.exists(path): - # TODO warning - return output - - sub_keys = list(kwargs.pop("subkeys", args)) - for sub_key in tuple(sub_keys): - _path = os.path.join(path, sub_key) - if not os.path.exists(_path): - break - - path = _path - sub_keys.pop(0) - - base_len = len(path) + 1 - for base, _directories, filenames in os.walk(path): - base_items_str = base[base_len:] - if not base_items_str: - base_items = [] - else: - base_items = base_items_str.split(os.path.sep) - - for filename in filenames: - basename, ext = os.path.splitext(filename) - if ext == ".json": - full_path = os.path.join(base, filename) - value = load_json(full_path) - dict_keys = base_items + [basename] - output = subkey_merge(output, value, dict_keys) - - for sub_key in sub_keys: - output = output[sub_key] - return output - - def studio_system_settings(): if os.path.exists(SYSTEM_SETTINGS_PATH): return load_json_file(SYSTEM_SETTINGS_PATH) From 39d83c59ecd9629bf5a151c0949874c13d474f01 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:19:17 +0200 Subject: [PATCH 076/219] path to project settings returns path to studio overrides if project name is None --- pype/settings/lib.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index de1aed31f6..a41b8fad8f 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -221,7 +221,9 @@ def studio_project_anatomy(): return {} -def path_to_project_overrides(project_name): +def path_to_project_settings(project_name): + if not project_name: + return PROJECT_SETTINGS_PATH return os.path.join( STUDIO_OVERRIDES_PATH, project_name, @@ -230,6 +232,8 @@ def path_to_project_overrides(project_name): def path_to_project_anatomy(project_name): + if not project_name: + return PROJECT_ANATOMY_PATH return os.path.join( STUDIO_OVERRIDES_PATH, project_name, @@ -241,7 +245,7 @@ def project_settings_overrides(project_name): if not project_name: return {} - path_to_json = path_to_project_overrides(project_name) + path_to_json = path_to_project_settings(project_name) if not os.path.exists(path_to_json): return {} return load_json_file(path_to_json) From 3697482fe15c83d097181ab1d5bc103cac08b9e5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:19:36 +0200 Subject: [PATCH 077/219] added functions for saving settings --- pype/settings/lib.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index a41b8fad8f..783347a7b4 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -241,6 +241,38 @@ def path_to_project_anatomy(project_name): ) +def save_studio_settings(data): + dirpath = os.path.dirname(SYSTEM_SETTINGS_PATH) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving studio overrides") + with open(SYSTEM_SETTINGS_PATH, "w") as file_stream: + json.dump(data, file_stream, indent=4) + + +def save_project_settings(project_name, overrides): + project_overrides_json_path = path_to_project_settings(project_name) + dirpath = os.path.dirname(project_overrides_json_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving overrides of project \"{}\"".format(project_name)) + with open(project_overrides_json_path, "w") as file_stream: + json.dump(overrides, file_stream, indent=4) + + +def save_project_anatomy(project_name, anatomy_data): + project_anatomy_json_path = path_to_project_anatomy(project_name) + dirpath = os.path.dirname(project_anatomy_json_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving anatomy of project \"{}\"".format(project_name)) + with open(project_anatomy_json_path, "w") as file_stream: + json.dump(anatomy_data, file_stream, indent=4) + + def project_settings_overrides(project_name): if not project_name: return {} From cdacc265721d43ed098dabc19f0b3895c06068cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:19:44 +0200 Subject: [PATCH 078/219] STUDIO_OVERRIDES_PATH is safer --- pype/settings/lib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 783347a7b4..ab3e3fcbe6 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -13,7 +13,7 @@ M_ENVIRONMENT_KEY = "__environment_keys__" M_POP_KEY = "__pop_key__" # Folder where studio overrides are stored -STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] +STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") # File where studio's system overrides are stored SYSTEM_SETTINGS_KEY = "system_settings" @@ -88,7 +88,6 @@ def load_json_file(fpath): "File has invalid json format \"{}\"".format(fpath), exc_info=True ) - return {} From b9567b0fe1e1ed744d80dc5801f5f2ade74e1d20 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:20:01 +0200 Subject: [PATCH 079/219] using saving functions in settings tool # Conflicts: # pype/tools/settings/settings/widgets/base.py --- pype/tools/settings/settings/widgets/base.py | 111 +++---------------- 1 file changed, 15 insertions(+), 96 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 3f842602ca..19336b1ac3 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -4,11 +4,8 @@ import json from Qt import QtWidgets, QtCore, QtGui from pype.settings.lib import ( SYSTEM_SETTINGS_KEY, - SYSTEM_SETTINGS_PATH, PROJECT_SETTINGS_KEY, - PROJECT_SETTINGS_PATH, PROJECT_ANATOMY_KEY, - PROJECT_ANATOMY_PATH, DEFAULTS_DIR, @@ -22,12 +19,9 @@ from pype.settings.lib import ( project_settings_overrides, project_anatomy_overrides, - path_to_project_overrides, - path_to_project_anatomy, - - apply_overrides, - find_environments, - DuplicatedEnvGroups + save_studio_settings, + save_project_settings, + save_project_anatomy ) from .widgets import UnsavedChangesDialog from . import lib @@ -222,16 +216,7 @@ class SystemWidget(QtWidgets.QWidget): values = lib.convert_gui_data_to_overrides(_data.get("system", {})) - if not self.duplicated_env_group_validation(overrides=values): - return - - dirpath = os.path.dirname(SYSTEM_SETTINGS_PATH) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to:", SYSTEM_SETTINGS_PATH) - with open(SYSTEM_SETTINGS_PATH, "w") as file_stream: - json.dump(values, file_stream, indent=4) + save_studio_settings(values) self._update_values() @@ -670,7 +655,7 @@ class ProjectWidget(QtWidgets.QWidget): has_invalid = True if not has_invalid: - return True + return self._save_overrides() invalid_items = [] for item in self.input_fields: @@ -688,16 +673,6 @@ class ProjectWidget(QtWidgets.QWidget): self.scroll_widget.ensureWidgetVisible(first_invalid_item) if first_invalid_item.isVisible(): first_invalid_item.setFocus(True) - return False - - def _save(self): - if not self.items_are_valid(): - return - - if self.project_name is None: - self._save_studio_overrides() - else: - self._save_overrides() def _on_refresh(self): self.reset() @@ -722,75 +697,19 @@ class ProjectWidget(QtWidgets.QWidget): ) # Saving overrides data - project_overrides_data = output_data.get( - PROJECT_SETTINGS_KEY, {} - ) - project_overrides_json_path = path_to_project_overrides( - self.project_name - ) - dirpath = os.path.dirname(project_overrides_json_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to:", project_overrides_json_path) - with open(project_overrides_json_path, "w") as file_stream: - json.dump(project_overrides_data, file_stream, indent=4) + project_overrides_data = output_data.get(PROJECT_SETTINGS_KEY, {}) + save_project_settings(self.project_name, project_overrides_data) # Saving anatomy data - project_anatomy_data = output_data.get( - PROJECT_ANATOMY_KEY, {} - ) - project_anatomy_json_path = path_to_project_anatomy( - self.project_name - ) - dirpath = os.path.dirname(project_anatomy_json_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) + project_anatomy_data = output_data.get(PROJECT_ANATOMY_KEY, {}) + save_project_anatomy(self.project_name, project_anatomy_data) - print("Saving data to:", project_anatomy_json_path) - with open(project_anatomy_json_path, "w") as file_stream: - json.dump(project_anatomy_data, file_stream, indent=4) - - # Refill values with overrides - self._on_project_change() - - def _save_studio_overrides(self): - data = {} - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not lib.NOT_SET: - data.update(value) - - output_data = lib.convert_gui_data_to_overrides( - data.get("project", {}) - ) - - # Project overrides data - project_overrides_data = output_data.get( - PROJECT_SETTINGS_KEY, {} - ) - dirpath = os.path.dirname(PROJECT_SETTINGS_PATH) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to:", PROJECT_SETTINGS_PATH) - with open(PROJECT_SETTINGS_PATH, "w") as file_stream: - json.dump(project_overrides_data, file_stream, indent=4) - - # Project Anatomy data - project_anatomy_data = output_data.get( - PROJECT_ANATOMY_KEY, {} - ) - dirpath = os.path.dirname(PROJECT_ANATOMY_PATH) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to:", PROJECT_ANATOMY_PATH) - with open(PROJECT_ANATOMY_PATH, "w") as file_stream: - json.dump(project_anatomy_data, file_stream, indent=4) - - # Update saved values - self._update_values() + if self.project_name: + # Refill values with overrides + self._on_project_change() + else: + # Update saved values + self._update_values() def _update_values(self): self.ignore_value_changes = True From 3a7cdae35cf5c1df18f68c345270693fef4fd6f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 3 Oct 2020 01:31:02 +0200 Subject: [PATCH 080/219] make sure settings.lib import won't crash if PYPE_PROJECT_CONFIGS is not set --- pype/settings/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index ab3e3fcbe6..59820d6639 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -13,7 +13,7 @@ M_ENVIRONMENT_KEY = "__environment_keys__" M_POP_KEY = "__pop_key__" # Folder where studio overrides are stored -STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") +STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") or "" # File where studio's system overrides are stored SYSTEM_SETTINGS_KEY = "system_settings" From fa46b8afd7c5a99df8b5cf4d522ba7a2a29205f1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 10:35:03 +0200 Subject: [PATCH 081/219] fixed saving of project settings # Conflicts: # pype/tools/settings/settings/widgets/base.py --- pype/tools/settings/settings/widgets/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 19336b1ac3..c6db3c34de 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -687,8 +687,12 @@ class ProjectWidget(QtWidgets.QWidget): return data = {} + studio_overrides = bool(self.project_name is None) for item in self.input_fields: - value, _is_group = item.overrides() + if studio_overrides: + value, is_group = item.studio_overrides() + else: + value, is_group = item.overrides() if value is not lib.NOT_SET: data.update(value) @@ -714,7 +718,7 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = default_values = lib.convert_data_to_gui_data( + default_values = lib.convert_data_to_gui_data( {"project": default_settings()} ) for input_field in self.input_fields: From e8e0261396a9b61dad0cd80769a12bec739deb44 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 11:24:05 +0200 Subject: [PATCH 082/219] safer work with default settings --- pype/settings/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 59820d6639..a998752287 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -324,13 +324,13 @@ def apply_overrides(source_data, override_data): def system_settings(): - default_values = default_settings()[SYSTEM_SETTINGS_KEY] + default_values = copy.deepcopy(default_settings()[SYSTEM_SETTINGS_KEY]) studio_values = studio_system_settings() return apply_overrides(default_values, studio_values) def project_settings(project_name): - default_values = default_settings()[PROJECT_SETTINGS_KEY] + default_values = copy.deepcopy(default_settings()[PROJECT_SETTINGS_KEY]) studio_values = studio_project_settings() studio_overrides = apply_overrides(default_values, studio_values) From 212bbbc8dffd6190ec1be2b2e921bd60874a79ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 11:24:27 +0200 Subject: [PATCH 083/219] print more specific data --- pype/settings/lib.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index a998752287..c47710a4d1 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -245,7 +245,9 @@ def save_studio_settings(data): if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving studio overrides") + print("Saving studio overrides. Output path: {}".format( + SYSTEM_SETTINGS_PATH + )) with open(SYSTEM_SETTINGS_PATH, "w") as file_stream: json.dump(data, file_stream, indent=4) @@ -256,7 +258,9 @@ def save_project_settings(project_name, overrides): if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving overrides of project \"{}\"".format(project_name)) + print("Saving overrides of project \"{}\". Output path: {}".format( + project_name, project_overrides_json_path + )) with open(project_overrides_json_path, "w") as file_stream: json.dump(overrides, file_stream, indent=4) @@ -267,7 +271,9 @@ def save_project_anatomy(project_name, anatomy_data): if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving anatomy of project \"{}\"".format(project_name)) + print("Saving anatomy of project \"{}\". Output path: {}".format( + project_name, project_anatomy_json_path + )) with open(project_anatomy_json_path, "w") as file_stream: json.dump(anatomy_data, file_stream, indent=4) From 4ae64993bcdb4d190971b00a94c2475c07b05dcf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 11:24:39 +0200 Subject: [PATCH 084/219] added few dostrings # Conflicts: # pype/settings/lib.py --- pype/settings/lib.py | 102 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index c47710a4d1..5fbf5d11dd 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -92,6 +92,35 @@ def load_json_file(fpath): def load_jsons_from_dir(path, *args, **kwargs): + """Load all json files with content from entered path. + + Enterd path hiearchy: + |_ folder1 + | |_ data1.json + |_ folder2 + |_ subfolder1 + |_ data2.json + + Will result in: + ```javascript + { + "folder1": { + "data1": "CONTENT OF FILE" + }, + "folder2": { + "data1": { + "subfolder1": "CONTENT OF FILE" + } + } + } + ``` + + Args: + path (str): Path to folder where jsons should be. + + Returns: + dict: loaded data + """ output = {} path = os.path.normpath(path) @@ -130,6 +159,15 @@ def load_jsons_from_dir(path, *args, **kwargs): def find_environments(data): + """ Find environemnt values from system settings by it's metadata. + + Args: + data(dict): System settings data or dictionary which may contain + environments metadata. + + Returns: + dict: Key as Environment key and value for `acre` module. + """ if not data or not isinstance(data, dict): return {} @@ -197,24 +235,28 @@ def subkey_merge(_dict, value, keys): def studio_system_settings(): + """Studio overrides of system settings.""" if os.path.exists(SYSTEM_SETTINGS_PATH): return load_json_file(SYSTEM_SETTINGS_PATH) return {} def studio_environments(): + """Environment values from defaults.""" if os.path.exists(ENVIRONMENTS_PATH): return load_json_file(ENVIRONMENTS_PATH) return {} def studio_project_settings(): + """Studio overrides of default project settings.""" if os.path.exists(PROJECT_SETTINGS_PATH): return load_json_file(PROJECT_SETTINGS_PATH) return {} def studio_project_anatomy(): + """Studio overrides of default project anatomy data.""" if os.path.exists(PROJECT_ANATOMY_PATH): return load_json_file(PROJECT_ANATOMY_PATH) return {} @@ -241,6 +283,14 @@ def path_to_project_anatomy(project_name): def save_studio_settings(data): + """Save studio overrides of system settings. + + Saving must corespond with loading. For loading should be used function + `studio_system_settings`. + + Args: + data(dict): Data of studio overrides with override metadata. + """ dirpath = os.path.dirname(SYSTEM_SETTINGS_PATH) if not os.path.exists(dirpath): os.makedirs(dirpath) @@ -253,6 +303,17 @@ def save_studio_settings(data): def save_project_settings(project_name, overrides): + """Save studio overrides of project settings. + + Data are saved for specific project or as defaults for all projects. + Saving must corespond with loading. For loading should be used functions + `project_settings_overrides` and `studio_project_settings`. + + Args: + project_name(str, null): Project name for which overrides are + or None for global settings. + data(dict): Data of project overrides with override metadata. + """ project_overrides_json_path = path_to_project_settings(project_name) dirpath = os.path.dirname(project_overrides_json_path) if not os.path.exists(dirpath): @@ -266,6 +327,17 @@ def save_project_settings(project_name, overrides): def save_project_anatomy(project_name, anatomy_data): + """Save studio overrides of project anatomy. + + Data are saved for specific project or as defaults for all projects. + Saving must corespond with loading. For loading should be used functions + `project_anatomy_overrides` and `studio_project_anatomy`. + + Args: + project_name(str, null): Project name for which overrides are + or None for global settings. + data(dict): Data of project overrides with override metadata. + """ project_anatomy_json_path = path_to_project_anatomy(project_name) dirpath = os.path.dirname(project_anatomy_json_path) if not os.path.exists(dirpath): @@ -279,6 +351,14 @@ def save_project_anatomy(project_name, anatomy_data): def project_settings_overrides(project_name): + """Studio overrides of project settings for specific project. + + Args: + project_name(str): Name of project for which data should be loaded. + + Returns: + dict: Only overrides for entered project, may be empty dictionary. + """ if not project_name: return {} @@ -289,6 +369,14 @@ def project_settings_overrides(project_name): def project_anatomy_overrides(project_name): + """Studio overrides of project anatomy for specific project. + + Args: + project_name(str): Name of project for which data should be loaded. + + Returns: + dict: Only overrides for entered project, may be empty dictionary. + """ if not project_name: return {} @@ -299,6 +387,7 @@ def project_anatomy_overrides(project_name): def merge_overrides(global_dict, override_dict): + """Merge override data to source data by metadata stored in.""" if M_OVERRIDEN_KEY in override_dict: overriden_keys = set(override_dict.pop(M_OVERRIDEN_KEY)) else: @@ -308,10 +397,7 @@ def merge_overrides(global_dict, override_dict): if value == M_POP_KEY: global_dict.pop(key) - elif ( - key in overriden_keys - or key not in global_dict - ): + elif (key in overriden_keys or key not in global_dict): global_dict[key] = value elif isinstance(value, dict) and isinstance(global_dict[key], dict): @@ -330,12 +416,14 @@ def apply_overrides(source_data, override_data): def system_settings(): + """System settings with applied studio overrides.""" default_values = copy.deepcopy(default_settings()[SYSTEM_SETTINGS_KEY]) studio_values = studio_system_settings() return apply_overrides(default_values, studio_values) def project_settings(project_name): + """Project settings with applied studio and project overrides.""" default_values = copy.deepcopy(default_settings()[PROJECT_SETTINGS_KEY]) studio_values = studio_project_settings() @@ -347,7 +435,11 @@ def project_settings(project_name): def environments(): - # TODO remove these defaults (All should be set with system settings) + """Environments from defaults and extracted from system settings. + + Returns: + dict: Output should be ready for `acre` module. + """ envs = copy.deepcopy(default_settings()[ENVIRONMENTS_KEY]) # This is part of loading environments from settings envs_from_system_settings = find_environments(system_settings()) From 170d2dc305b7ba84eb5069a5a795e3bd73bbe387 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 12:00:10 +0200 Subject: [PATCH 085/219] modified docstring in save functions to be more clear --- pype/settings/lib.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 5fbf5d11dd..34603ef30e 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -285,8 +285,9 @@ def path_to_project_anatomy(project_name): def save_studio_settings(data): """Save studio overrides of system settings. - Saving must corespond with loading. For loading should be used function - `studio_system_settings`. + Do not use to store whole system settings data with defaults but only it's + overrides with metadata defining how overrides should be applied in load + function. For loading should be used function `studio_system_settings`. Args: data(dict): Data of studio overrides with override metadata. @@ -306,8 +307,12 @@ def save_project_settings(project_name, overrides): """Save studio overrides of project settings. Data are saved for specific project or as defaults for all projects. - Saving must corespond with loading. For loading should be used functions - `project_settings_overrides` and `studio_project_settings`. + + Do not use to store whole project settings data with defaults but only it's + overrides with metadata defining how overrides should be applied in load + function. For loading should be used functions `studio_project_settings` + for global project settings and `project_settings_overrides` for + project specific settings. Args: project_name(str, null): Project name for which overrides are @@ -329,9 +334,11 @@ def save_project_settings(project_name, overrides): def save_project_anatomy(project_name, anatomy_data): """Save studio overrides of project anatomy. - Data are saved for specific project or as defaults for all projects. - Saving must corespond with loading. For loading should be used functions - `project_anatomy_overrides` and `studio_project_anatomy`. + Do not use to store whole project anatomy data with defaults but only it's + overrides with metadata defining how overrides should be applied in load + function. For loading should be used functions `studio_project_anatomy` + for global project settings and `project_anatomy_overrides` for + project specific settings. Args: project_name(str, null): Project name for which overrides are From fd00cf196bea78a92ac734059de6fd839118ee32 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 20:17:27 +0200 Subject: [PATCH 086/219] modified first docstring by comments --- pype/settings/lib.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 34603ef30e..2614d55fbe 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -92,9 +92,12 @@ def load_json_file(fpath): def load_jsons_from_dir(path, *args, **kwargs): - """Load all json files with content from entered path. + """Load all .json files with content from entered folder path. - Enterd path hiearchy: + Data are loaded recursively from a directory and recreate the + hierarchy as a dictionary. + + Entered path hiearchy: |_ folder1 | |_ data1.json |_ folder2 @@ -116,10 +119,10 @@ def load_jsons_from_dir(path, *args, **kwargs): ``` Args: - path (str): Path to folder where jsons should be. + path (str): Path to the root folder where the json hierarchy starts. Returns: - dict: loaded data + dict: Loaded data. """ output = {} From cc9574981986235b2d787273aa2c9092a164a713 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 19 Oct 2020 13:27:48 +0200 Subject: [PATCH 087/219] Small fixes in docstrings --- pype/settings/lib.py | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 2614d55fbe..adb5b2626f 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -23,9 +23,6 @@ SYSTEM_SETTINGS_PATH = os.path.join( # File where studio's environment overrides are stored ENVIRONMENTS_KEY = "environments" -ENVIRONMENTS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, ENVIRONMENTS_KEY + ".json" -) # File where studio's default project overrides are stored PROJECT_SETTINGS_KEY = "project_settings" @@ -244,13 +241,6 @@ def studio_system_settings(): return {} -def studio_environments(): - """Environment values from defaults.""" - if os.path.exists(ENVIRONMENTS_PATH): - return load_json_file(ENVIRONMENTS_PATH) - return {} - - def studio_project_settings(): """Studio overrides of default project settings.""" if os.path.exists(PROJECT_SETTINGS_PATH): @@ -335,13 +325,7 @@ def save_project_settings(project_name, overrides): def save_project_anatomy(project_name, anatomy_data): - """Save studio overrides of project anatomy. - - Do not use to store whole project anatomy data with defaults but only it's - overrides with metadata defining how overrides should be applied in load - function. For loading should be used functions `studio_project_anatomy` - for global project settings and `project_anatomy_overrides` for - project specific settings. + """Save studio overrides of project anatomy data. Args: project_name(str, null): Project name for which overrides are @@ -396,8 +380,9 @@ def project_anatomy_overrides(project_name): return load_json_file(path_to_json) -def merge_overrides(global_dict, override_dict): - """Merge override data to source data by metadata stored in.""" +def merge_overrides(source_dict, override_dict): + """Merge data from override_dict to source_dict.""" + if M_OVERRIDEN_KEY in override_dict: overriden_keys = set(override_dict.pop(M_OVERRIDEN_KEY)) else: @@ -405,17 +390,17 @@ def merge_overrides(global_dict, override_dict): for key, value in override_dict.items(): if value == M_POP_KEY: - global_dict.pop(key) + source_dict.pop(key) - elif (key in overriden_keys or key not in global_dict): - global_dict[key] = value + elif (key in overriden_keys or key not in source_dict): + source_dict[key] = value - elif isinstance(value, dict) and isinstance(global_dict[key], dict): - global_dict[key] = merge_overrides(global_dict[key], value) + elif isinstance(value, dict) and isinstance(source_dict[key], dict): + source_dict[key] = merge_overrides(source_dict[key], value) else: - global_dict[key] = value - return global_dict + source_dict[key] = value + return source_dict def apply_overrides(source_data, override_data): @@ -445,7 +430,10 @@ def project_settings(project_name): def environments(): - """Environments from defaults and extracted from system settings. + """Calculated environment based on defaults and system settings. + + Any default environment also found in the system settings will be fully + overriden by the one from the system settings. Returns: dict: Output should be ready for `acre` module. From 0ebd6466e90ee138a2c5386e49a65951e0ebe6ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 03:32:30 +0100 Subject: [PATCH 088/219] updated lib with new modifications --- pype/settings/lib.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index adb5b2626f..a50d7793b5 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -158,7 +158,7 @@ def load_jsons_from_dir(path, *args, **kwargs): return output -def find_environments(data): +def find_environments(data, with_items=False, parents=None): """ Find environemnt values from system settings by it's metadata. Args: @@ -412,14 +412,14 @@ def apply_overrides(source_data, override_data): def system_settings(): """System settings with applied studio overrides.""" - default_values = copy.deepcopy(default_settings()[SYSTEM_SETTINGS_KEY]) + default_values = default_settings()[SYSTEM_SETTINGS_KEY] studio_values = studio_system_settings() return apply_overrides(default_values, studio_values) def project_settings(project_name): """Project settings with applied studio and project overrides.""" - default_values = copy.deepcopy(default_settings()[PROJECT_SETTINGS_KEY]) + default_values = default_settings()[PROJECT_SETTINGS_KEY] studio_values = studio_project_settings() studio_overrides = apply_overrides(default_values, studio_values) @@ -438,6 +438,7 @@ def environments(): Returns: dict: Output should be ready for `acre` module. """ + # TODO remove these defaults (All should be set with system settings) envs = copy.deepcopy(default_settings()[ENVIRONMENTS_KEY]) # This is part of loading environments from settings envs_from_system_settings = find_environments(system_settings()) From b873c9bc8221fe368cc46eda0865387f58f86d34 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 03:33:49 +0100 Subject: [PATCH 089/219] updated base with new changes --- pype/tools/settings/settings/widgets/base.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index c6db3c34de..3e1c6ea6b9 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -21,7 +21,11 @@ from pype.settings.lib import ( save_studio_settings, save_project_settings, - save_project_anatomy + save_project_anatomy, + + apply_overrides, + find_environments, + DuplicatedEnvGroups ) from .widgets import UnsavedChangesDialog from . import lib @@ -216,6 +220,9 @@ class SystemWidget(QtWidgets.QWidget): values = lib.convert_gui_data_to_overrides(_data.get("system", {})) + if not self.duplicated_env_group_validation(overrides=values): + return + save_studio_settings(values) self._update_values() @@ -528,7 +535,7 @@ class ProjectWidget(QtWidgets.QWidget): layout.addWidget(project_list_widget, 0) layout.addWidget(configurations_widget, 1) - save_btn.clicked.connect(self._save) + save_btn.clicked.connect(self._save_overrides) project_list_widget.project_changed.connect(self._on_project_change) self.project_list_widget = project_list_widget @@ -655,7 +662,7 @@ class ProjectWidget(QtWidgets.QWidget): has_invalid = True if not has_invalid: - return self._save_overrides() + return True invalid_items = [] for item in self.input_fields: @@ -673,6 +680,7 @@ class ProjectWidget(QtWidgets.QWidget): self.scroll_widget.ensureWidgetVisible(first_invalid_item) if first_invalid_item.isVisible(): first_invalid_item.setFocus(True) + return False def _on_refresh(self): self.reset() From f20c8d142ea953abe492f11fd7438b1cabb9d29e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 03:43:34 +0100 Subject: [PATCH 090/219] fixed refresh of project widgets --- pype/tools/settings/settings/widgets/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 3e1c6ea6b9..653da51d43 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -563,11 +563,11 @@ class ProjectWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() def reset(self): - if self.content_layout.count() != 0: - for widget in self.input_fields: - self.content_layout.removeWidget(widget) - widget.deleteLater() - self.input_fields.clear() + self.input_fields.clear() + while self.content_layout.count() != 0: + widget = self.content_layout.itemAt(0).widget() + self.content_layout.removeWidget(widget) + widget.deleteLater() self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) From 154005006878941378bd18a8492dc72317f95276 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 11:23:11 +0100 Subject: [PATCH 091/219] modified `initial_attributes` renamed variable `input_data` to `schema_data` which are stored to item also "key" is required key --- .../settings/settings/widgets/item_types.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 472a5b111c..eacfb94046 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -134,7 +134,7 @@ class SettingObject: role_name = self.user_role return role_name in self._roles - def initial_attributes(self, input_data, parent, as_widget): + def initial_attributes(self, schema_data, parent, as_widget): """Prepare attributes based on entered arguments. This method should be same for each item type. Few item types @@ -142,23 +142,25 @@ class SettingObject: """ self._set_default_attributes() + self.schema_data = schema_data + self._parent = parent self._as_widget = as_widget - self._roles = input_data.get("roles") + self._roles = schema_data.get("roles") if self._roles is not None and not isinstance(self._roles, list): self._roles = [self._roles] - self._is_group = input_data.get("is_group", False) - self._env_group_key = input_data.get("env_group_key") + self._is_group = schema_data.get("is_group", False) + self._env_group_key = schema_data.get("env_group_key") # TODO not implemented yet - self._is_nullable = input_data.get("is_nullable", False) + self._is_nullable = schema_data.get("is_nullable", False) if self.is_environ: if not self.allow_to_environment: raise TypeError(( "Item {} does not allow to store environment values" - ).format(input_data["type"])) + ).format(schema_data["type"])) self.add_environ_field(self) @@ -178,6 +180,9 @@ class SettingObject: self.hide() self.hidden_by_role = True + if not self.as_widget: + self.key = self.schema_data["key"] + @property def user_role(self): """Tool is running with any user role. From 76bfb6a578ff1a8e0e2389e84cc328241a170260 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 11:31:50 +0100 Subject: [PATCH 092/219] split items creation to initialization in `__init__` and `create_ui` methods --- .../settings/widgets/anatomy_types.py | 18 +- pype/tools/settings/settings/widgets/base.py | 2 + .../settings/settings/widgets/item_types.py | 307 +++++++++--------- 3 files changed, 160 insertions(+), 167 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index e1a726187c..3eb2bc0d3e 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -34,7 +34,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): } def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, schema_data, parent, as_widget=False ): if as_widget: raise TypeError( @@ -43,11 +43,12 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): super(AnatomyWidget, self).__init__(parent) self.setObjectName("AnatomyWidget") - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) - self.key = input_data["key"] + self.key = schema_data["key"] - children_data = input_data["children"] + def create_ui(self, label_widget=None): + children_data = self.schema_data["children"] roots_input_data = {} templates_input_data = {} for child in children_data: @@ -269,6 +270,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): path_widget_data, self, as_widget=True, parent_widget=content_widget ) + singleroot_widget.create_ui() multiroot_data = { "key": self.key, "expandable": False, @@ -281,6 +283,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): multiroot_data, self, as_widget=True, parent_widget=content_widget ) + multiroot_widget.create_ui() content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(0, 0, 0, 0) @@ -638,10 +641,9 @@ class TemplatesWidget(QtWidgets.QWidget, SettingObject): } self.body_widget = body_widget self.label_widget = body_widget.label_widget - self.value_input = RawJsonWidget( - template_input_data, self, - label_widget=self.label_widget - ) + self.value_input = RawJsonWidget(template_input_data, self) + self.value_input.create_ui(label_widget=self.label_widget) + content_layout.addWidget(self.value_input) layout = QtWidgets.QVBoxLayout(self) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 3f842602ca..ff35dc254c 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -321,6 +321,7 @@ class SystemWidget(QtWidgets.QWidget): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) item = klass(child_configuration, self) + item.create_ui() self.input_fields.append(item) self.content_layout.addWidget(item, 0) @@ -587,6 +588,7 @@ class ProjectWidget(QtWidgets.QWidget): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) item = klass(child_configuration, self) + item.create_ui() self.input_fields.append(item) self.content_layout.addWidget(item, 0) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index eacfb94046..387398a32b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -916,7 +916,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent @@ -924,17 +924,16 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.initial_attributes(input_data, parent, as_widget) + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self.as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(label_widget, 0) + if not self.as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + layout.addWidget(label_widget, 0) self.label_widget = label_widget checkbox_height = self.style().pixelMetric( @@ -969,34 +968,32 @@ class NumberWidget(QtWidgets.QWidget, InputObject): valid_value_types = (int, float) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(NumberWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) kwargs = { - modifier: input_data.get(modifier) + modifier: self.schema_data.get(modifier) for modifier in self.input_modifiers - if input_data.get(modifier) + if self.schema_data.get(modifier) } self.input_field = NumberSpinBox(self, **kwargs) self.setFocusProxy(self.input_field) - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) + if not self._as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) self.label_widget = label_widget layout.addWidget(self.input_field, 1) @@ -1017,17 +1014,17 @@ class TextWidget(QtWidgets.QWidget, InputObject): valid_value_types = (str, ) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(TextWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) - self.multiline = input_data.get("multiline", False) - placeholder = input_data.get("placeholder") + self.initial_attributes(schema_data, parent, as_widget) + self.multiline = schema_data.get("multiline", False) + self.placeholder_text = schema_data.get("placeholder") + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -1037,8 +1034,8 @@ class TextWidget(QtWidgets.QWidget, InputObject): else: self.input_field = QtWidgets.QLineEdit(self) - if placeholder: - self.input_field.setPlaceholderText(placeholder) + if self.placeholder_text: + self.input_field.setPlaceholderText(self.placeholder_text) self.setFocusProxy(self.input_field) @@ -1046,12 +1043,10 @@ class TextWidget(QtWidgets.QWidget, InputObject): if self.multiline: layout_kwargs["alignment"] = QtCore.Qt.AlignTop - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0, **layout_kwargs) + if not self._as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0, **layout_kwargs) self.label_widget = label_widget layout.addWidget(self.input_field, 1, **layout_kwargs) @@ -1078,25 +1073,23 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): valid_value_types = (str, ) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(PathInputWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) + if not self._as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) self.label_widget = label_widget self.input_field = PathInput(self) @@ -1122,34 +1115,32 @@ class EnumeratorWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(EnumeratorWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) - self.multiselection = input_data.get("multiselection") - self.enum_items = input_data["enum_items"] + self.initial_attributes(schema_data, parent, as_widget) + self.multiselection = schema_data.get("multiselection") + self.enum_items = schema_data["enum_items"] if not self.enum_items: raise ValueError("Attribute `enum_items` is not defined.") + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(label_widget, 0) + if not self._as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + layout.addWidget(label_widget, 0) self.label_widget = label_widget if self.multiselection: - placeholder = input_data.get("placeholder") + placeholder = self.schema_data.get("placeholder") self.input_field = MultiSelectionComboBox( placeholder=placeholder, parent=self ) @@ -1276,15 +1267,17 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): allow_to_environment = True def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(RawJsonWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) + # By default must be invalid + self._is_invalid = True + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -1294,16 +1287,13 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding ) - self._is_invalid = self.input_field.has_invalid_value() self.setFocusProxy(self.input_field) - if not self.as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) + if not self.as_widget and not label_widget: + label = self.schema_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget layout.addWidget(self.input_field, 1, alignment=QtCore.Qt.AlignTop) @@ -1408,9 +1398,9 @@ class ListItem(QtWidgets.QWidget, SettingObject): self.value_input = ItemKlass( item_schema, self, - as_widget=True, - label_widget=None + as_widget=True ) + self.value_input.create_ui() layout.addWidget(self.value_input, 1) @@ -1540,17 +1530,18 @@ class ListWidget(QtWidgets.QWidget, InputObject): valid_value_types = (list, ) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(ListWidget, self).__init__(parent_widget) self.setObjectName("ListWidget") - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) - object_type = input_data["object_type"] + self.input_fields = [] + + object_type = schema_data["object_type"] if isinstance(object_type, dict): self.item_schema = object_type else: @@ -1558,7 +1549,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): "type": object_type } # Backwards compatibility - input_modifiers = input_data.get("input_modifiers") or {} + input_modifiers = schema_data.get("input_modifiers") or {} if input_modifiers: self.log.warning(( "Used deprecated key `input_modifiers` to define item." @@ -1566,17 +1557,14 @@ class ListWidget(QtWidgets.QWidget, InputObject): )) self.item_schema.update(input_modifiers) - self.input_fields = [] - + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 5) layout.setSpacing(5) - if not self.as_widget: - self.key = input_data["key"] - if not label_widget: - label_widget = QtWidgets.QLabel(input_data["label"], self) - layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) + if not self.as_widget and not label_widget: + label_widget = QtWidgets.QLabel(self.schema_data["label"], self) + layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget @@ -1788,39 +1776,39 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): valid_value_types = (list, ) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(ListStrictWidget, self).__init__(parent_widget) self.setObjectName("ListStrictWidget") - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) + + self.is_horizontal = schema_data.get("horizontal", True) + self.object_types = self.schema_data["object_types"] self.input_fields = [] + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 5) layout.setSpacing(5) - if not self.as_widget: - self.key = input_data["key"] - if not label_widget: - label_widget = QtWidgets.QLabel(input_data["label"], self) - layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) + if not self.as_widget and not label_widget: + label_widget = QtWidgets.QLabel(self.schema_data["label"], self) + layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget - self._add_children(layout, input_data) + self._add_children(layout) - def _add_children(self, layout, input_data): + def _add_children(self, layout): inputs_widget = QtWidgets.QWidget(self) inputs_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout.addWidget(inputs_widget) - horizontal = input_data.get("horizontal", True) - if horizontal: + if self.is_horizontal: inputs_layout = QtWidgets.QHBoxLayout(inputs_widget) else: inputs_layout = QtWidgets.QGridLayout(inputs_widget) @@ -1832,7 +1820,7 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): self.inputs_layout = inputs_layout children_item_mapping = [] - for child_configuration in input_data["object_types"]: + for child_configuration in self.object_types: item_widget = ListItem( child_configuration, self, self.inputs_widget, is_strict=True ) @@ -1847,7 +1835,7 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): children_item_mapping.append((label_widget, item_widget)) - if horizontal: + if self.is_horizontal: self._add_children_horizontally(children_item_mapping) else: self._add_children_vertically(children_item_mapping) @@ -1977,9 +1965,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.value_input = ItemKlass( item_schema, self, - as_widget=True, - label_widget=None + as_widget=True ) + self.value_input.create_ui() + self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") @@ -2138,22 +2127,25 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): valid_value_types = (dict, ) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(ModifiableDict, self).__init__(parent_widget) self.setObjectName("ModifiableDict") - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) self.input_fields = [] - self.key = input_data["key"] - self.value_is_env_group = input_data.get("value_is_env_group") or False + # Validation of "key" key + self.key = schema_data["key"] + self.value_is_env_group = ( + schema_data.get("value_is_env_group") or False + ) + self.hightlight_content = self.schema_data.get("highlight_content") - object_type = input_data["object_type"] + object_type = schema_data["object_type"] if isinstance(object_type, dict): self.item_schema = object_type else: @@ -2161,7 +2153,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.item_schema = { "type": object_type } - input_modifiers = input_data.get("input_modifiers") or {} + input_modifiers = schema_data.get("input_modifiers") or {} if input_modifiers: self.log.warning(( "Used deprecated key `input_modifiers` to define item." @@ -2172,7 +2164,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if self.value_is_env_group: self.item_schema["env_group_key"] = "" - if input_data.get("highlight_content", False): + def create_ui(self, label_widget=None): + if self.hightlight_content: content_state = "hightlighted" bottom_margin = 5 else: @@ -2183,9 +2176,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) - label = input_data.get("label") + label = self.schema_data.get("label") - if as_widget: + if self.as_widget: body_widget = None self.label_widget = label_widget @@ -2193,7 +2186,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): body_widget = None self.label_widget = None else: - body_widget = ExpandingWidget(input_data["label"], self) + body_widget = ExpandingWidget(self.schema_data["label"], self) main_layout.addWidget(body_widget) self.label_widget = body_widget.label_widget @@ -2221,9 +2214,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.content_layout = content_layout if body_widget: - collapsable = input_data.get("collapsable", True) + collapsable = self.schema_data.get("collapsable", True) if collapsable: - collapsed = input_data.get("collapsed", True) + collapsed = self.schema_data.get("collapsed", True) if not collapsed: body_widget.toggle_content() @@ -2416,29 +2409,31 @@ class DictWidget(QtWidgets.QWidget, SettingObject): valid_value_types = (dict, type(NOT_SET)) def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(DictWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) self.input_fields = [] self.checkbox_widget = None - self.checkbox_key = input_data.get("checkbox_key") + self.checkbox_key = schema_data.get("checkbox_key") - if not self.as_widget: - self.key = input_data["key"] + self.highlight_content = schema_data.get("highlight_content", False) + self.show_borders = schema_data.get("show_borders", True) + self.collapsable = schema_data.get("collapsable", True) + self.collapsed = schema_data.get("collapsed", True) - if not self.as_widget and input_data.get("label") is None: + def create_ui(self, label_widget=None): + if not self.as_widget and self.schema_data.get("label") is None: self._ui_item_without_label() else: - self._ui_item_or_as_widget(input_data, label_widget) + self._ui_item_or_as_widget(label_widget) - for child_data in input_data.get("children", []): + for child_data in self.schema_data.get("children", []): self.add_children_gui(child_data) any_visible = False @@ -2464,14 +2459,12 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.content_layout.setContentsMargins(0, 0, 0, 0) self.content_layout.setSpacing(5) - def _ui_item_or_as_widget(self, input_data, label_widget): + def _ui_item_or_as_widget(self, label_widget): content_widget = QtWidgets.QWidget(self) if self.as_widget: content_widget.setObjectName("DictAsWidgetBody") - show_borders = str( - int(input_data.get("show_borders", True)) - ) + show_borders = str(int(self.show_borders)) content_widget.setProperty("show_borders", show_borders) content_layout_margins = (5, 5, 5, 5) main_layout_spacing = 5 @@ -2479,7 +2472,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): else: content_widget.setObjectName("ContentWidget") - if input_data.get("highlight_content", False): + if self.highlight_content: content_state = "hightlighted" bottom_margin = 5 else: @@ -2489,7 +2482,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin) main_layout_spacing = 0 - body_widget = ExpandingWidget(input_data["label"], self) + body_widget = ExpandingWidget(self.schema_data["label"], self) label_widget = body_widget.label_widget body_widget.set_content_widget(content_widget) @@ -2509,13 +2502,11 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.content_layout = content_layout if body_widget: - collapsable = input_data.get("collapsable", True) if len(self.input_fields) == 1 and self.checkbox_widget: body_widget.hide_toolbox(hide_content=True) - elif collapsable: - collapsed = input_data.get("collapsed", True) - if not collapsed: + elif self.collapsable: + if not self.collapsed: body_widget.toggle_content() else: body_widget.hide_toolbox(hide_content=False) @@ -2547,13 +2538,14 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return self._add_checkbox_child(child_configuration) label_widget = None - if not klass.expand_in_grid: + item = klass(child_configuration, self) + if not item.expand_in_grid: label = child_configuration.get("label") if label is not None: label_widget = GridLabelWidget(label, self) self.content_layout.addWidget(label_widget, row, 0, 1, 1) - item = klass(child_configuration, self, label_widget=label_widget) + item.create_ui(label_widget=label_widget) item.value_changed.connect(self._on_value_change) if label_widget: @@ -2569,8 +2561,9 @@ class DictWidget(QtWidgets.QWidget, SettingObject): def _add_checkbox_child(self, child_configuration): item = BooleanWidget( - child_configuration, self, label_widget=self.label_widget + child_configuration, self ) + item.create_ui(label_widget=self.label_widget) item.value_changed.connect(self._on_value_change) self.body_widget.add_widget_before_label(item) @@ -2872,14 +2865,13 @@ class PathWidget(QtWidgets.QWidget, SettingObject): } def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(PathWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) # This is partial input and dictionary input if not self.any_parent_is_group and not self._as_widget: @@ -2887,22 +2879,20 @@ class PathWidget(QtWidgets.QWidget, SettingObject): else: self._is_group = False - self.multiplatform = input_data.get("multiplatform", False) - self.multipath = input_data.get("multipath", False) + self.multiplatform = schema_data.get("multiplatform", False) + self.multipath = schema_data.get("multipath", False) self.input_field = None + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self.as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) + if not self.as_widget and not label_widget: + label_widget = QtWidgets.QLabel(self.schema_data["label"]) + label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget self.content_widget = QtWidgets.QWidget(self) @@ -2912,7 +2902,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): layout.addWidget(self.content_widget) - self.create_gui() + self.create_ui_inputs() @property def default_input_value(self): @@ -2928,13 +2918,12 @@ class PathWidget(QtWidgets.QWidget, SettingObject): } return value_type() - def create_gui(self): + def create_ui_inputs(self): if not self.multiplatform and not self.multipath: input_data = {"key": self.key} - path_input = PathInputWidget( - input_data, self, - as_widget=True, label_widget=self.label_widget - ) + path_input = PathInputWidget(input_data, self, as_widget=True) + path_input.create_ui(label_widget=self.label_widget) + self.setFocusProxy(path_input) self.content_layout.addWidget(path_input) self.input_field = path_input @@ -2946,10 +2935,8 @@ class PathWidget(QtWidgets.QWidget, SettingObject): "key": self.key, "object_type": "path-input" } - input_widget = ListWidget( - item_schema, self, - as_widget=True, label_widget=self.label_widget - ) + input_widget = ListWidget(item_schema, self, as_widget=True) + input_widget.create_ui(label_widget=self.label_widget) self.setFocusProxy(input_widget) self.content_layout.addWidget(input_widget) self.input_field = input_widget @@ -2975,9 +2962,9 @@ class PathWidget(QtWidgets.QWidget, SettingObject): item_schema["children"].append(child_item) - input_widget = DictWidget( - item_schema, self, as_widget=True, label_widget=self.label_widget - ) + input_widget = DictWidget(item_schema, self, as_widget=True) + input_widget.create_ui(label_widget=self.label_widget) + self.content_layout.addWidget(input_widget) self.input_field = input_widget input_widget.value_changed.connect(self._on_value_change) @@ -3221,23 +3208,24 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): expand_in_grid = True def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None + self, schema_data, parent, as_widget=False, parent_widget=None ): if parent_widget is None: parent_widget = parent super(DictFormWidget, self).__init__(parent_widget) - self.initial_attributes(input_data, parent, as_widget) + self.initial_attributes(schema_data, parent, as_widget) self._as_widget = False self._is_group = False self.input_fields = [] + + def create_ui(self, label_widget=None): self.content_layout = QtWidgets.QFormLayout(self) self.content_layout.setContentsMargins(0, 0, 0, 0) - for child_data in input_data.get("children", []): + for child_data in self.schema_data.get("children", []): self.add_children_gui(child_data) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) @@ -3260,7 +3248,8 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): label_widget = FormLabel(label, self) - item = klass(child_configuration, self, label_widget=label_widget) + item = klass(child_configuration, self) + item.create_ui(label_widget=label_widget) label_widget.item = item if item.hidden_by_role: From 5877e2246d8b324b87520b1a251b3a30f077512b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 11:49:30 +0100 Subject: [PATCH 093/219] added `is_wrapper_item` to determine if item may have any clue about it's `key` --- .../settings/settings/widgets/item_types.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 387398a32b..318f264f40 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -37,6 +37,9 @@ class SettingObject: """Partially abstract class for Setting's item type workflow.""" # `is_input_type` attribute says if has implemented item type methods is_input_type = True + # `is_wrapper_item` attribute says if item will never hold `key` + # information and is just visually different + is_wrapper_item = False # Each input must have implemented default value for development # when defaults are not filled yet. default_input_value = NOT_SET @@ -180,7 +183,17 @@ class SettingObject: self.hide() self.hidden_by_role = True - if not self.as_widget: + if ( + self.is_input_type + and not self.as_widget + and not self.is_wrapper_item + ): + if "key" not in self.schema_data: + error_msg = "Missing \"key\" in schema data. {}".format( + str(schema_data).replace("'", '"') + ) + raise KeyError(error_msg) + self.key = self.schema_data["key"] @property @@ -3206,6 +3219,7 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) allow_actions = False expand_in_grid = True + is_wrapper_item = True def __init__( self, schema_data, parent, as_widget=False, parent_widget=None From 5d66a25b78e87b94c1c11caea0c6eb2ff552c903 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 27 Nov 2020 12:05:21 +0100 Subject: [PATCH 094/219] change name of category --- .../gui_schemas/projects_schema/schema_anatomy_imageio.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json index b7de6c5091..58852ba821 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json @@ -1,7 +1,7 @@ { "type": "dict", "key": "imageio", - "label": "Color Management (Image i/o)", + "label": "Color Management and Output Formats", "is_file": true, "children": [{ "key": "hosts", From 7b1acfd60ddbf0fefdaa581ec614ece18fe3ade9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 27 Nov 2020 12:07:50 +0100 Subject: [PATCH 095/219] feat(imageio): dropping `host` parent as it is redundant --- .../defaults/project_anatomy/imageio.json | 249 +++++---- .../schema_anatomy_imageio.json | 509 +++++++++--------- 2 files changed, 370 insertions(+), 388 deletions(-) diff --git a/pype/settings/defaults/project_anatomy/imageio.json b/pype/settings/defaults/project_anatomy/imageio.json index 1adab0fc2b..98ded33370 100644 --- a/pype/settings/defaults/project_anatomy/imageio.json +++ b/pype/settings/defaults/project_anatomy/imageio.json @@ -1,131 +1,122 @@ { - "hosts": { - "hiero": { - "workfile": { - "ocioConfigName": "nuke-default", - "ocioconfigpath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB" - }, - "regexInputs": { - "inputs": [ - { - "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", - "colorspace": "sRGB" - } - ] - } - }, - "nuke": { - "workfile": { - "colorManagement": "Nuke", - "OCIO_config": "nuke-default", - "customOCIOConfigPath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpaceLUT": "linear", - "monitorLut": "sRGB", - "int8Lut": "sRGB", - "int16Lut": "sRGB", - "logLut": "Cineon", - "floatLut": "linear" - }, - "nodes": { - "requiredNodes": [ - { - "plugins": [ - "CreateWriteRender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "name": "file_type", - "value": "exr" - }, - { - "name": "datatype", - "value": "16 bit half" - }, - { - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "name": "autocrop", - "value": "True" - }, - { - "name": "tile_color", - "value": "0xff0000ff" - }, - { - "name": "channels", - "value": "rgb" - }, - { - "name": "colorspace", - "value": "linear" - } - ] - }, - { - "plugins": [ - "CreateWritePrerender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "name": "file_type", - "value": "exr" - }, - { - "name": "datatype", - "value": "16 bit half" - }, - { - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "name": "autocrop", - "value": "False" - }, - { - "name": "tile_color", - "value": "0xff0000ff" - }, - { - "name": "channels", - "value": "rgb" - }, - { - "name": "colorspace", - "value": "linear" - } - ] - } - ], - "customNodes": [] - }, - "regexInputs": { - "inputs": [ - { - "regex": "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]", - "colorspace": "linear" - } - ] - } - } + "hiero": { + "workfile": { + "ocioConfigName": "nuke-default", + "ocioconfigpath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpace": "linear", + "sixteenBitLut": "sRGB", + "eightBitLut": "sRGB", + "floatLut": "linear", + "logLut": "Cineon", + "viewerLut": "sRGB", + "thumbnailLut": "sRGB" + }, + "regexInputs": { + "inputs": [{ + "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", + "colorspace": "sRGB" + }] } -} \ No newline at end of file + }, + "nuke": { + "workfile": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "customOCIOConfigPath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpaceLUT": "linear", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "nodes": { + "requiredNodes": [{ + "plugins": [ + "CreateWriteRender" + ], + "nukeNodeClass": "Write", + "knobs": [{ + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "True" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + }, + { + "plugins": [ + "CreateWritePrerender" + ], + "nukeNodeClass": "Write", + "knobs": [{ + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "False" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + } + ], + "customNodes": [] + }, + "regexInputs": { + "inputs": [{ + "regex": "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]", + "colorspace": "linear" + }] + } + } +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json index b7de6c5091..2057adfbbc 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json @@ -4,294 +4,285 @@ "label": "Color Management (Image i/o)", "is_file": true, "children": [{ - "key": "hosts", + "key": "hiero", "type": "dict", - "label": "Hosts", - "collapsed": false, - "collapsable": true, + "label": "Hiero", "children": [{ - "key": "hiero", + "key": "workfile", "type": "dict", - "label": "Hiero", + "label": "Workfile", + "collapsable": false, "children": [{ - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsable": false, + "type": "form", "children": [{ - "type": "form", - "children": [{ - "type": "enum", - "key": "ocioConfigName", - "label": "OpenColorIO Config", - "enum_items": [{ - "nuke-default": "nuke-default" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "custom": "custom" - } - ] - }, { - "type": "path-widget", - "key": "ocioconfigpath", - "label": "Custom OCIO path", - "multiplatform": true, - "multipath": true - }, { - "type": "text", - "key": "workingSpace", - "label": "Working Space" - }, { - "type": "text", - "key": "sixteenBitLut", - "label": "16 Bit Files" - }, { - "type": "text", - "key": "eightBitLut", - "label": "8 Bit Files" - }, { - "type": "text", - "key": "floatLut", - "label": "Floating Point Files" - }, { - "type": "text", - "key": "logLut", - "label": "Log Files" - }, { - "type": "text", - "key": "viewerLut", - "label": "Viewer" - }, { - "type": "text", - "key": "thumbnailLut", - "label": "Thumbnails" - }] - }] - }, { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsable": true, - "children": [{ - "type": "list", - "key": "inputs", - "label": "", - "object_type": { - "type": "dict", - "children": [{ - "type": "text", - "key": "regex", - "label": "Regex" - }, { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - }] - } + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [{ + "nuke-default": "nuke-default" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, { + "type": "path-widget", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" }] }] }, { - "key": "nuke", + "key": "regexInputs", "type": "dict", - "label": "Nuke", + "label": "Colorspace on Inputs by regex detection", + "collapsable": true, "children": [{ - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsable": false, - "is_group": true, - "children": [{ - "type": "form", + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", "children": [{ - "type": "enum", - "key": "colorManagement", - "label": "color management", - "enum_items": [{ - "Nuke": "Nuke" - }, - { - "OCIO": "OCIO" - } - ] - }, { - "type": "enum", - "key": "OCIO_config", - "label": "OpenColorIO Config", - "enum_items": [{ - "nuke-default": "nuke-default" - }, - { - "spi-vfx": "spi-vfx" - }, - { - "spi-anim": "spi-anim" - }, - { - "aces_1.0.3": "aces_0.1.1" - }, - { - "aces_1.0.3": "aces_0.7.1" - }, - { - "aces_1.0.3": "aces_1.0.1" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "custom": "custom" - } - ] - }, { - "type": "path-widget", - "key": "customOCIOConfigPath", - "label": "Custom OCIO config path", - "multiplatform": true, - "multipath": true + "type": "text", + "key": "regex", + "label": "Regex" }, { "type": "text", - "key": "workingSpaceLUT", - "label": "Working Space" - }, { - "type": "text", - "key": "monitorLut", - "label": "monitor" - }, { - "type": "text", - "key": "int8Lut", - "label": "8-bit files" - }, { - "type": "text", - "key": "int16Lut", - "label": "16-bit files" - }, { - "type": "text", - "key": "logLut", - "label": "log files" - }, { - "type": "text", - "key": "floatLut", - "label": "float files" + "key": "colorspace", + "label": "Colorspace" }] + } + }] + }] + }, { + "key": "nuke", + "type": "dict", + "label": "Nuke", + "children": [{ + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsable": false, + "is_group": true, + "children": [{ + "type": "form", + "children": [{ + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [{ + "Nuke": "Nuke" + }, + { + "OCIO": "OCIO" + } + ] + }, { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [{ + "nuke-default": "nuke-default" + }, + { + "spi-vfx": "spi-vfx" + }, + { + "spi-anim": "spi-anim" + }, + { + "aces_1.0.3": "aces_0.1.1" + }, + { + "aces_1.0.3": "aces_0.7.1" + }, + { + "aces_1.0.3": "aces_1.0.1" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, { + "type": "path-widget", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, { + "type": "text", + "key": "logLut", + "label": "log files" + }, { + "type": "text", + "key": "floatLut", + "label": "float files" }] - }, { - "key": "nodes", - "type": "dict", - "label": "Nodes", - "collapsable": true, - "is_group": true, - "children": [ - { - "key": "requiredNodes", + }] + }, { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsable": true, + "is_group": true, + "children": [{ + "key": "requiredNodes", + "type": "list", + "label": "Required Nodes", + "object_type": { + "type": "dict", + "children": [{ "type": "list", - "label": "Required Nodes", + "key": "plugins", + "label": "Used in plugins", "object_type": { - "type": "dict", - "children": [{ - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass", - "label": "Plugin Class" - } - }, { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, { - "type": "splitter" - }, { - "key": "knobs", - "label": "Knobs", - "type": "list", - "object_type": { - "type": "dict", - "children": [{ - "type": "text", - "key": "name", - "label": "Name" - }, { - "type": "text", - "key": "value", - "label": "Value" - }] - } - }] + "type": "text", + "key": "pluginClass", + "label": "Plugin Class" } }, { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, { + "type": "splitter" + }, { + "key": "knobs", + "label": "Knobs", "type": "list", - "key": "customNodes", - "label": "Custom Nodes", "object_type": { "type": "dict", "children": [{ - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass", - "label": "Plugin Class" - } + "type": "text", + "key": "name", + "label": "Name" }, { "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, { - "type": "splitter" - }, { - "key": "knobs", - "label": "Knobs", - "type": "list", - "object_type": { - "type": "dict", - "children": [{ - "type": "text", - "key": "name", - "label": "Name" - }, { - "type": "text", - "key": "value", - "label": "Value" - }] - } + "key": "value", + "label": "Value" }] } - } - ] + }] + } }, { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsable": true, - "children": [{ - "type": "list", - "key": "inputs", - "label": "", - "object_type": { - "type": "dict", - "children": [{ + "type": "list", + "key": "customNodes", + "label": "Custom Nodes", + "object_type": { + "type": "dict", + "children": [{ + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { "type": "text", - "key": "regex", - "label": "Regex" - }, { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - }] - } - }] + "key": "pluginClass", + "label": "Plugin Class" + } + }, { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, { + "type": "splitter" + }, { + "key": "knobs", + "label": "Knobs", + "type": "list", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "name", + "label": "Name" + }, { + "type": "text", + "key": "value", + "label": "Value" + }] + } + }] + } + }] + }, { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsable": true, + "children": [{ + "type": "list", + "key": "inputs", + "label": "", + "object_type": { + "type": "dict", + "children": [{ + "type": "text", + "key": "regex", + "label": "Regex" + }, { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + }] + } }] }] }] From ae657498e3dfaf5db3b273519cb5a4e07ef7b1e7 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 27 Nov 2020 12:30:39 +0100 Subject: [PATCH 096/219] fix extract look file check --- pype/plugins/maya/publish/extract_look.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/extract_look.py b/pype/plugins/maya/publish/extract_look.py index 6bd202093f..7a5dc25fcf 100644 --- a/pype/plugins/maya/publish/extract_look.py +++ b/pype/plugins/maya/publish/extract_look.py @@ -350,7 +350,7 @@ class ExtractLook(pype.api.Extractor): if existing and not force: self.log.info("Found hash in database, preparing hardlink..") source = next((p for p in existing if os.path.exists(p)), None) - if filepath: + if source: return source, HARDLINK, texture_hash else: self.log.warning( From 7b025ffc830ac027c62227579204d1593415faf5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Nov 2020 13:07:26 +0100 Subject: [PATCH 097/219] Committing again after rebase Add pulling extension from AE Render Queue Add pulling information about audio layer for composition --- .../stubs/aftereffects_server_stub.py | 32 ++++++++++++++++ .../aftereffects/publish/collect_audio.py | 27 +++++++++++++ .../aftereffects/publish/collect_render.py | 31 ++++++++++----- .../publish/submit_aftereffects_deadline.py | 38 ++++++++++++------- 4 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 pype/plugins/aftereffects/publish/collect_audio.py diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index 84dce39a41..e47aaee471 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -252,6 +252,9 @@ class AfterEffectsServerStub(): Args: item_id (int): + Returns: + (namedtuple) + """ res = self.websocketserver.call(self.client.call ('AfterEffects.get_work_area', @@ -305,6 +308,35 @@ class AfterEffectsServerStub(): image_path=project_path, as_copy=as_copy)) + def get_render_info(self): + """ Get render queue info for render purposes + + Returns: + (namedtuple): with 'file_name' field + """ + res = self.websocketserver.call(self.client.call + ('AfterEffects.get_render_info')) + + records = self._to_records(res) + if records: + return records.pop() + + log.debug("Couldn't get render queue info") + + def get_audio_url(self, item_id): + """ Get audio layer absolute url for comp + + Args: + item_id (int): composition id + Returns: + (str): absolute path url + """ + res = self.websocketserver.call(self.client.call + ('AfterEffects.get_audio_url', + item_id=item_id)) + + return res + def close(self): self.client.close() diff --git a/pype/plugins/aftereffects/publish/collect_audio.py b/pype/plugins/aftereffects/publish/collect_audio.py new file mode 100644 index 0000000000..37938eb6a6 --- /dev/null +++ b/pype/plugins/aftereffects/publish/collect_audio.py @@ -0,0 +1,27 @@ +import os + +import pyblish.api + +from avalon import aftereffects + + +class CollectAudio(pyblish.api.ContextPlugin): + """Inject audio file url for rendered composition into context. + Needs to run AFTER 'collect_render'. Use collected comp_id to check + if there is an AVLayer in this composition + """ + + order = pyblish.api.CollectorOrder + 0.499 + label = "Collect Audio" + hosts = ["aftereffects"] + + def process(self, context): + for instance in context: + if instance.data["family"] == 'render.farm': + comp_id = instance.data["comp_id"] + if not comp_id: + self.log.debug("No comp_id filled in instance") + return + context.data["audioFile"] = os.path.normpath( + aftereffects.stub().get_audio_url(comp_id) + ).replace("\\", "/") diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index 13ffc3f208..f053eb7ce3 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -11,6 +11,7 @@ from avalon import aftereffects class AERenderInstance(RenderInstance): # extend generic, composition name is needed comp_name = attr.ib(default=None) + comp_id = attr.ib(default=None) class CollectAERender(abstract_collect_render.AbstractCollectRender): @@ -83,6 +84,7 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): raise ValueError("There is no composition for item {}". format(item_id)) instance.comp_name = comp.name + instance.comp_id = item_id instance._anatomy = context.data["anatomy"] instance.anatomyData = context.data["anatomyData"] @@ -108,18 +110,29 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): start = render_instance.frameStart end = render_instance.frameEnd + # pull file name from Render Queue Output module + render_q = aftereffects.stub().get_render_info() + _, ext = os.path.splitext(os.path.basename(render_q.file_name)) base_dir = self._get_output_dir(render_instance) expected_files = [] - for frame in range(start, end + 1): - path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( - render_instance.asset, - render_instance.subset, - "v{:03d}".format(render_instance.version), - str(frame).zfill(self.padding_width), - self.rendered_extension - )) + if "#" not in render_q.file_name: # single frame (mov)W + path = os.path.join(base_dir, "{}_{}_{}.{}".format( + render_instance.asset, + render_instance.subset, + "v{:03d}".format(render_instance.version), + ext.replace('.', '') + )) expected_files.append(path) - + else: + for frame in range(start, end + 1): + path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( + render_instance.asset, + render_instance.subset, + "v{:03d}".format(render_instance.version), + str(frame).zfill(self.padding_width), + ext.replace('.', '') + )) + expected_files.append(path) return expected_files def _get_output_dir(self, render_instance): diff --git a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py index 15d9e216fb..8bb42a0239 100644 --- a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py +++ b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py @@ -18,6 +18,7 @@ class DeadlinePluginInfo(): ProjectPath = attr.ib(default=None) AWSAssetFile0 = attr.ib(default=None) Version = attr.ib(default=None) + MultiProcess = attr.ib(default=None) class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): @@ -39,9 +40,14 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline dln_job_info.Plugin = "AfterEffects" dln_job_info.UserName = context.data.get( "deadlineUser", getpass.getuser()) - frame_range = "{}-{}".format(self._instance.data["frameStart"], - self._instance.data["frameEnd"]) - dln_job_info.Frames = frame_range + if self._instance.data["frameEnd"] > self._instance.data["frameStart"]: + frame_range = "{}-{}".format(self._instance.data["frameStart"], + self._instance.data["frameEnd"]) + dln_job_info.Frames = frame_range + + if len(self._instance.data["expectedFiles"]) == 1: + dln_job_info.ChunkSize = 1000000 + dln_job_info.OutputFilename = \ os.path.basename(self._instance.data["expectedFiles"][0]) dln_job_info.OutputDirectory = \ @@ -77,17 +83,23 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline script_path = context.data["currentFile"] render_path = self._instance.data["expectedFiles"][0] - # replace frame info ('000001') with Deadline's required '[#######]' - # expects filename in format project_asset_subset_version.FRAME.ext - render_dir = os.path.dirname(render_path) - file_name = os.path.basename(render_path) - arr = file_name.split('.') - assert len(arr) == 3, \ - "Unable to parse frames from {}".format(file_name) - hashed = '[{}]'.format(len(arr[1]) * "#") - render_path = os.path.join(render_dir, - '{}.{}.{}'.format(arr[0], hashed, arr[2])) + if len(self._instance.data["expectedFiles"]) > 1: + # replace frame ('000001') with Deadline's required '[#######]' + # expects filename in format project_asset_subset_version.FRAME.ext + render_dir = os.path.dirname(render_path) + file_name = os.path.basename(render_path) + arr = file_name.split('.') + assert len(arr) == 3, \ + "Unable to parse frames from {}".format(file_name) + hashed = '[{}]'.format(len(arr[1]) * "#") + + render_path = os.path.join(render_dir, + '{}.{}.{}'.format(arr[0], hashed, + arr[2])) + deadline_plugin_info.MultiProcess = True + else: + deadline_plugin_info.MultiProcess = False deadline_plugin_info.Comp = self._instance.data["comp_name"] deadline_plugin_info.Version = "17.5" From 170d1b01883ae9103f90e0f538b118f5e2b5e2ea Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:25:56 +0100 Subject: [PATCH 098/219] fixed Expanding widget --- pype/tools/settings/settings/widgets/widgets.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index b64b1aa8ac..6135f8dcb2 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -112,7 +112,7 @@ class ExpandingWidget(QtWidgets.QWidget): before_label_layout.setContentsMargins(0, 0, 0, 0) after_label_widget = QtWidgets.QWidget(side_line_widget) - after_label_layout = QtWidgets.QVBoxLayout(after_label_widget) + after_label_layout = QtWidgets.QHBoxLayout(after_label_widget) after_label_layout.setContentsMargins(0, 0, 0, 0) spacer_widget = QtWidgets.QWidget(side_line_widget) @@ -158,6 +158,12 @@ class ExpandingWidget(QtWidgets.QWidget): self.content_widget.setVisible(not hide_content) self.parent().updateGeometry() + def show_toolbox(self): + self.toolbox_hidden = False + self.toggle_content(self.button_toggle.isChecked()) + + self.parent().updateGeometry() + def set_content_widget(self, content_widget): content_widget.setVisible(False) self.main_layout.addWidget(content_widget) From 3c0de1cbd42a1e64af76d700580eb41e6e971431 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:33:38 +0100 Subject: [PATCH 099/219] preparations for labeled item --- .../settings/settings/widgets/item_types.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 318f264f40..f83e406815 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1970,11 +1970,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - ItemKlass = TypeToKlass.types[item_schema["type"]] self.key_input = QtWidgets.QLineEdit(self) self.key_input.setObjectName("DictKey") + ItemKlass = TypeToKlass.types[item_schema["type"]] self.value_input = ItemKlass( item_schema, self, @@ -2013,6 +2013,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.origin_key = NOT_SET + @property + def labeled_items(self): + return self._parent.labeled_items + def key_value(self): return self.key_input.text() @@ -2028,8 +2032,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return False def _on_key_change(self): + value = self.key_input.text() if self.value_is_env_group: - self.value_input.env_group_key = self.key_input.text() + self.value_input.env_group_key = value self._on_value_change() def _on_value_change(self, item=None): @@ -2066,6 +2071,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._parent.add_row(row=self.row() + 1) def on_remove_clicked(self): + self.remove_row() + + def remove_row(self): self._parent.remove_row(self) def set_as_empty(self, is_empty=True): @@ -2156,7 +2164,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.value_is_env_group = ( schema_data.get("value_is_env_group") or False ) - self.hightlight_content = self.schema_data.get("highlight_content") + self.hightlight_content = schema_data.get("highlight_content") or False + self.labeled_items = schema_data.get("labeled_items") or False object_type = schema_data["object_type"] if isinstance(object_type, dict): @@ -2365,15 +2374,24 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.input_fields.insert(row, item_widget) previous_input = None - for input_field in self.input_fields: - if previous_input is not None: + if self.labeled_items: + for input_field in self.input_fields: + if previous_input is not None: + self.setTabOrder( + previous_input, input_field.value_input + ) + previous_input = input_field.value_input.focusProxy() + + else: + for input_field in self.input_fields: + if previous_input is not None: + self.setTabOrder( + previous_input, input_field.key_input + ) + previous_input = input_field.value_input.focusProxy() self.setTabOrder( - previous_input, input_field.key_input + input_field.key_input, previous_input ) - previous_input = input_field.value_input.focusProxy() - self.setTabOrder( - input_field.key_input, previous_input - ) # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` From 6f6678a5bf50c5dbb6f5c9fcb14fda6ee227e97d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:35:58 +0100 Subject: [PATCH 100/219] moved key input --- pype/tools/settings/settings/widgets/item_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index f83e406815..c35ae96635 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1971,8 +1971,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): layout.setSpacing(3) - self.key_input = QtWidgets.QLineEdit(self) - self.key_input.setObjectName("DictKey") ItemKlass = TypeToKlass.types[item_schema["type"]] self.value_input = ItemKlass( @@ -1984,6 +1982,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") + self.key_input = QtWidgets.QLineEdit(self.wrapper_widget or self) + self.key_input.setObjectName("DictKey") self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) From d7289f0fc091ca7b9d4a94bd99f21a70b624c9ce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:36:15 +0100 Subject: [PATCH 101/219] different init if labeld item --- .../settings/settings/widgets/item_types.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index c35ae96635..716b63cf91 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1966,11 +1966,25 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._is_empty = False self.is_key_duplicated = False - layout = QtWidgets.QHBoxLayout(self) + if self.labeled_items: + layout = QtWidgets.QVBoxLayout(self) + else: + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) + self.wrapper_widget = None + if self.labeled_items: + self.wrapper_widget = ExpandingWidget("", self) + layout.addWidget(self.wrapper_widget) + content_widget = QtWidgets.QWidget(self.wrapper_widget) + content_layout = QtWidgets.QHBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(3) + + self.wrapper_widget.set_content_widget(content_widget) ItemKlass = TypeToKlass.types[item_schema["type"]] self.value_input = ItemKlass( From c9c23658112a10f053a09378fda9d7236f80217f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:48:15 +0100 Subject: [PATCH 102/219] set_as_empty has separated logic --- .../settings/settings/widgets/item_types.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 716b63cf91..aa265caec9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2093,10 +2093,22 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def set_as_empty(self, is_empty=True): self._is_empty = is_empty - self.key_input.setVisible(not is_empty) self.value_input.setVisible(not is_empty) - self.remove_btn.setEnabled(not is_empty) - self.spacer_widget.setVisible(is_empty) + self.add_btn.setVisible(is_empty) + if not self.labeled_items: + self.key_input.setVisible(not is_empty) + self.remove_btn.setEnabled(not is_empty) + self.spacer_widget.setVisible(is_empty) + + else: + self.key_input.setVisible(is_empty) + self.edit_btn.setVisible(not is_empty) + + self.wrapper_widget.label_widget.setVisible(not is_empty) + if is_empty: + self.wrapper_widget.hide_toolbox() + else: + self.wrapper_widget.show_toolbox() self._on_value_change() @property From aae2d448dd4f14e88eee8eb17dea5b788bb55251 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:49:00 +0100 Subject: [PATCH 103/219] add_clicked has separated logic --- .../settings/settings/widgets/item_types.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index aa265caec9..e16fd0700c 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2079,10 +2079,21 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._parent.is_group def on_add_clicked(self): - if self._is_empty: - self.set_as_empty(False) - else: - self._parent.add_row(row=self.row() + 1) + if not self.labeled_items: + if self._is_empty: + self.set_as_empty(False) + else: + self._parent.add_row(row=self.row() + 1) + return + + if not self._is_empty: + return + + if not self.key_value(): + return + + self.set_as_empty(False) + self._parent.add_row(row=self.row() + 1, is_empty=True) def on_remove_clicked(self): self.remove_row() From ea19cfcae075b8d46ece5e242422e0fc03310310 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:49:22 +0100 Subject: [PATCH 104/219] change_key_label implemented to change label --- pype/tools/settings/settings/widgets/item_types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e16fd0700c..db8f8aa973 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2049,6 +2049,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): value = self.key_input.text() if self.value_is_env_group: self.value_input.env_group_key = value + self.change_key_label(value) self._on_value_change() def _on_value_change(self, item=None): @@ -2078,6 +2079,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def is_group(self): return self._parent.is_group + def change_key_label(self, label): + if self.wrapper_widget: + self.wrapper_widget.label_widget.setText(label) + def on_add_clicked(self): if not self.labeled_items: if self._is_empty: From 96b8916147d2cf1780df8c7ed4cd7a103023e971 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:50:06 +0100 Subject: [PATCH 105/219] implemented empty `on_edit_clicked` method --- pype/tools/settings/settings/widgets/item_types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index db8f8aa973..aac2c6aa37 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2100,6 +2100,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_as_empty(False) self._parent.add_row(row=self.row() + 1, is_empty=True) + def on_edit_clicked(self): + print("Edit triggered") + def on_remove_clicked(self): self.remove_row() From f69ec9f45ef04d9bbb185c1800a665ef2a7e3b2f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 14:50:19 +0100 Subject: [PATCH 106/219] separated item initialization --- .../settings/settings/widgets/item_types.py | 109 +++++++++++------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index aac2c6aa37..265911f914 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1966,6 +1966,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._is_empty = False self.is_key_duplicated = False + self.origin_key = NOT_SET + if self.labeled_items: layout = QtWidgets.QVBoxLayout(self) else: @@ -1974,58 +1976,87 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - self.wrapper_widget = None - if self.labeled_items: - self.wrapper_widget = ExpandingWidget("", self) - layout.addWidget(self.wrapper_widget) - - content_widget = QtWidgets.QWidget(self.wrapper_widget) - content_layout = QtWidgets.QHBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) - content_layout.setSpacing(3) - - self.wrapper_widget.set_content_widget(content_widget) - ItemKlass = TypeToKlass.types[item_schema["type"]] - self.value_input = ItemKlass( + value_input = ItemKlass( item_schema, self, as_widget=True ) - self.value_input.create_ui() + value_input.create_ui() - self.add_btn = QtWidgets.QPushButton("+") - self.remove_btn = QtWidgets.QPushButton("-") - self.key_input = QtWidgets.QLineEdit(self.wrapper_widget or self) - self.key_input.setObjectName("DictKey") + wrapper_widget = None + if self.labeled_items: + wrapper_widget = ExpandingWidget("", self) + layout.addWidget(wrapper_widget) - self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + content_widget = QtWidgets.QWidget(wrapper_widget) + content_layout = QtWidgets.QHBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(3) - self.add_btn.setProperty("btn-type", "tool-item") - self.remove_btn.setProperty("btn-type", "tool-item") + wrapper_widget.set_content_widget(content_widget) - self.spacer_widget = QtWidgets.QWidget(self) - self.spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - self.spacer_widget.setVisible(False) + content_layout.addWidget(value_input) - layout.addWidget(self.add_btn, 0) - layout.addWidget(self.remove_btn, 0) - layout.addWidget(self.key_input, 0) - layout.addWidget(self.spacer_widget, 1) - layout.addWidget(self.value_input, 1) + key_input = QtWidgets.QLineEdit(wrapper_widget or self) + key_input.setObjectName("DictKey") - self.setFocusProxy(self.value_input) + spacer_widget = None + if not self.labeled_items: + spacer_widget = QtWidgets.QWidget(self) + spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + spacer_widget.setVisible(False) - self.add_btn.setFixedSize(self._btn_size, self._btn_size) - self.remove_btn.setFixedSize(self._btn_size, self._btn_size) - self.add_btn.clicked.connect(self.on_add_clicked) - self.remove_btn.clicked.connect(self.on_remove_clicked) + add_btn = QtWidgets.QPushButton("+") + add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + add_btn.setProperty("btn-type", "tool-item") + add_btn.setFixedSize(self._btn_size, self._btn_size) - self.key_input.textChanged.connect(self._on_key_change) - self.value_input.value_changed.connect(self._on_value_change) + edit_btn = None + remove_btn = None + if self.labeled_items: + edit_btn = QtWidgets.QPushButton("Edit") + edit_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + edit_btn.setProperty("btn-type", "tool-item") + edit_btn.setFixedSize(self._btn_size, self._btn_size) + else: + remove_btn = QtWidgets.QPushButton("-") + remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + remove_btn.setProperty("btn-type", "tool-item") + remove_btn.setFixedSize(self._btn_size, self._btn_size) - self.origin_key = NOT_SET + if self.labeled_items: + wrapper_widget.add_widget_after_label(key_input) + wrapper_widget.add_widget_after_label(add_btn) + wrapper_widget.add_widget_after_label(edit_btn) + + else: + layout.addWidget(add_btn, 0) + layout.addWidget(remove_btn, 0) + layout.addWidget(key_input, 0) + layout.addWidget(spacer_widget, 1) + layout.addWidget(value_input, 1) + + self.setFocusProxy(value_input) + + key_input.textChanged.connect(self._on_key_change) + value_input.value_changed.connect(self._on_value_change) + add_btn.clicked.connect(self.on_add_clicked) + if edit_btn: + edit_btn.clicked.connect(self.on_edit_clicked) + if remove_btn: + remove_btn.clicked.connect(self.on_remove_clicked) + + self.key_input = key_input + self.value_input = value_input + + self.wrapper_widget = wrapper_widget + + self.spacer_widget = spacer_widget + + self.add_btn = add_btn + self.edit_btn = edit_btn + self.remove_btn = remove_btn @property def labeled_items(self): @@ -2101,7 +2132,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._parent.add_row(row=self.row() + 1, is_empty=True) def on_edit_clicked(self): - print("Edit triggered") + pass def on_remove_clicked(self): self.remove_row() From 6a98818834f663ca147001e94c61827514004ded Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 15:03:13 +0100 Subject: [PATCH 107/219] handledimplemented basic adding new items --- .../settings/settings/widgets/item_types.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 265911f914..e678651545 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2000,6 +2000,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_input = QtWidgets.QLineEdit(wrapper_widget or self) key_input.setObjectName("DictKey") + if self.labeled_items: + def focused_out(event): + super(QtWidgets.QLineEdit, key_input).focusOutEvent(event) + self._on_enter_press() + key_input.focusOutEvent = focused_out spacer_widget = None if not self.labeled_items: @@ -2040,6 +2045,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.setFocusProxy(value_input) key_input.textChanged.connect(self._on_key_change) + key_input.returnPressed.connect(self._on_enter_press) value_input.value_changed.connect(self._on_value_change) add_btn.clicked.connect(self.on_add_clicked) if edit_btn: @@ -2076,6 +2082,15 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return True return False + def _on_enter_press(self): + if not self.labeled_items: + return + + if self._is_empty: + self.on_add_clicked() + else: + self.set_edit_mode(False) + def _on_key_change(self): value = self.key_input.text() if self.value_is_env_group: @@ -2132,7 +2147,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._parent.add_row(row=self.row() + 1, is_empty=True) def on_edit_clicked(self): - pass + self.set_edit_mode() + + def set_edit_mode(self, enabled=True): + self.key_input.setVisible(enabled) + self.wrapper_widget.label_widget.setVisible(not enabled) def on_remove_clicked(self): self.remove_row() From 06413e5a1bf0a525a0e687438d81a9d14c3e71f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 15:09:58 +0100 Subject: [PATCH 108/219] fixed state styles --- pype/tools/settings/settings/widgets/item_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e678651545..cd586a71ea 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1998,7 +1998,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): content_layout.addWidget(value_input) - key_input = QtWidgets.QLineEdit(wrapper_widget or self) + key_input = QtWidgets.QLineEdit(self) key_input.setObjectName("DictKey") if self.labeled_items: def focused_out(event): From f6885cdd9cadc41cbeb89e58f99efa95a6d5ebf9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 15:10:16 +0100 Subject: [PATCH 109/219] fixed focus out --- pype/tools/settings/settings/widgets/item_types.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index cd586a71ea..e23390a7e4 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2002,8 +2002,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_input.setObjectName("DictKey") if self.labeled_items: def focused_out(event): - super(QtWidgets.QLineEdit, key_input).focusOutEvent(event) + QtWidgets.QLineEdit.focusOutEvent(key_input, event) self._on_enter_press() + key_input.focusOutEvent = focused_out spacer_widget = None @@ -2150,8 +2151,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_edit_mode() def set_edit_mode(self, enabled=True): - self.key_input.setVisible(enabled) self.wrapper_widget.label_widget.setVisible(not enabled) + self.key_input.setVisible(enabled) + if enabled: + self.key_input.setFocus() def on_remove_clicked(self): self.remove_row() From 098b2e295ba1d66d448d7a01fb8850e0721a3c72 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 27 Nov 2020 15:14:19 +0100 Subject: [PATCH 110/219] handle vray aov names --- pype/hosts/maya/expected_files.py | 62 +++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index bd58b10d74..344d32f144 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -418,13 +418,12 @@ class AExpectedFiles: if connections: for connection in connections: if connection: - node_name = connection.split(".")[0] - if cmds.nodeType(node_name) == "renderLayer": - attr_name = "%s.value" % ".".join( - connection.split(".")[:-1] - ) - if node_name == layer: - yield cmds.getAttr(attr_name) + # node_name = connection.split(".")[0] + + attr_name = "%s.value" % ".".join( + connection.split(".")[:-1] + ) + yield cmds.getAttr(attr_name) def get_render_attribute(self, attr): """Get attribute from render options. @@ -642,25 +641,42 @@ class ExpectedFilesVray(AExpectedFiles): return enabled_aovs def _get_vray_aov_name(self, node): + """Get AOVs name from Vray. - # Get render element pass type - vray_node_attr = next( - attr - for attr in cmds.listAttr(node) - if attr.startswith("vray_name") - ) - pass_type = vray_node_attr.rsplit("_", 1)[-1] + Args: + node (str): aov node name. - # Support V-Ray extratex explicit name (if set by user) - if pass_type == "extratex": - explicit_attr = "{}.vray_explicit_name_extratex".format(node) - explicit_name = cmds.getAttr(explicit_attr) - if explicit_name: - return explicit_name + Returns: + str: aov name. - # Node type is in the attribute name but we need to check if value - # of the attribute as it can be changed - return cmds.getAttr("{}.{}".format(node, vray_node_attr)) + """ + vray_name = None + vray_explicit_name = None + vray_file_name = None + for attr in cmds.listAttr(node): + if attr.startswith("vray_filename"): + vray_file_name = cmds.getAttr("{}.{}".format(node, attr)) + elif attr.startswith("vray_name"): + vray_name = cmds.getAttr("{}.{}".format(node, attr)) + elif attr.startswith("vray_explicit_name"): + vray_explicit_name = cmds.getAttr("{}.{}".format(node, attr)) + + if vray_file_name is not None and vray_file_name != "": + final_name = vray_file_name + elif vray_explicit_name is not None and vray_explicit_name != "": + final_name = vray_explicit_name + elif vray_name is not None and vray_name != "": + final_name = vray_name + else: + continue + # special case for Material Select elements - these are named based on the material + # they are connected to. + if "vray_mtl_mtlselect" in cmds.listAttr(node): + connections = cmds.listConnections("{}.vray_mtl_mtlselect".format(node)) + if connections: + final_name += '_{}'.format(str(connections[0])) + + return final_name class ExpectedFilesRedshift(AExpectedFiles): From caf4d94b567a0ea922c18a185c9fc9979b3649b6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 15:29:33 +0100 Subject: [PATCH 111/219] key dulication is handled better --- .../settings/settings/widgets/item_types.py | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e23390a7e4..775408cb25 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1964,7 +1964,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._any_parent_is_group = True self._is_empty = False - self.is_key_duplicated = False + self._is_key_duplicated = False self.origin_key = NOT_SET @@ -2079,10 +2079,19 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): if self.key_value() == "": return True - if self.is_key_duplicated: + if self._is_key_duplicated: return True return False + def set_key_is_duplicated(self, duplicated): + if duplicated == self._is_key_duplicated: + return + + self._is_key_duplicated = duplicated + if duplicated and self.labeled_items: + self.set_edit_mode(True) + self.update_style() + def _on_enter_press(self): if not self.labeled_items: return @@ -2151,6 +2160,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_edit_mode() def set_edit_mode(self, enabled=True): + if self.is_invalid and not enabled: + return self.wrapper_widget.label_widget.setVisible(not enabled) self.key_input.setVisible(enabled) if enabled: @@ -2375,13 +2386,10 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): for fields in fields_by_keys.values(): if len(fields) == 1: field = fields[0] - if field.is_key_duplicated: - field.is_key_duplicated = False - field.update_style() + field.set_key_is_duplicated(False) else: for field in fields: - field.is_key_duplicated = True - field.update_style() + field.set_key_is_duplicated(True) if self.is_overidable: self._is_overriden = True From 8c6ad61256c7b2cc27a196abc169bb4cfa8cc8f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 15:29:44 +0100 Subject: [PATCH 112/219] content layout has better margins --- pype/tools/settings/settings/widgets/item_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 775408cb25..8b689ecd1a 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1991,8 +1991,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): content_widget = QtWidgets.QWidget(wrapper_widget) content_layout = QtWidgets.QHBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) - content_layout.setSpacing(3) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + content_layout.setSpacing(5) wrapper_widget.set_content_widget(content_widget) From c5ca4d10bbb6a8bae9a749cb7fde1d6a5bcd1184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 27 Nov 2020 16:03:40 +0100 Subject: [PATCH 113/219] hound stuff --- pype/hosts/maya/expected_files.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index 344d32f144..0b9b7d21d1 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -583,7 +583,7 @@ class ExpectedFilesVray(AExpectedFiles): expected_files[0].update(update) return expected_files - + def get_aovs(self): """Get all AOVs. @@ -669,10 +669,11 @@ class ExpectedFilesVray(AExpectedFiles): final_name = vray_name else: continue - # special case for Material Select elements - these are named based on the material - # they are connected to. + # special case for Material Select elements - these are named + # based on the materia they are connected to. if "vray_mtl_mtlselect" in cmds.listAttr(node): - connections = cmds.listConnections("{}.vray_mtl_mtlselect".format(node)) + connections = cmds.listConnections( + "{}.vray_mtl_mtlselect".format(node)) if connections: final_name += '_{}'.format(str(connections[0])) From 598297de935bd5cd151c711a3f081f828993465d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 16:26:57 +0100 Subject: [PATCH 114/219] audio may not be added to output based on tag "no-audio" in output definition --- pype/plugins/global/publish/extract_review.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 857119176f..d08748209d 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -280,6 +280,15 @@ class ExtractReview(pyblish.api.InstancePlugin): handles_are_set = handle_start > 0 or handle_end > 0 + with_audio = True + if ( + # Check if has `no-audio` tag + "no-audio" in output_def["tags"] + # Check if instance has ny audio in data + or not instance.data.get("audio") + ): + with_audio = False + return { "fps": float(instance.data["fps"]), "frame_start": frame_start, @@ -295,6 +304,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "resolution_height": instance.data.get("resolutionHeight"), "origin_repre": repre, "input_is_sequence": self.input_is_sequence(repre), + "with_audio": with_audio, "without_handles": without_handles, "handles_are_set": handles_are_set } @@ -389,7 +399,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args.append("-shortest") # Add audio arguments if there are any. Skipped when output are images. - if not temp_data["output_ext_is_image"]: + if not temp_data["output_ext_is_image"] and temp_data["with_audio"]: audio_in_args, audio_filters, audio_out_args = self.audio_args( instance, temp_data ) From b7480065c5c0b55dd015b79fd8c8faedfeefeb0a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 16:44:30 +0100 Subject: [PATCH 115/219] few minor fixes --- .../settings/settings/widgets/item_types.py | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8b689ecd1a..cb7a310945 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1984,6 +1984,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): ) value_input.create_ui() + key_input = QtWidgets.QLineEdit(self) + key_input.setObjectName("DictKey") + wrapper_widget = None if self.labeled_items: wrapper_widget = ExpandingWidget("", self) @@ -1998,12 +2001,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): content_layout.addWidget(value_input) - key_input = QtWidgets.QLineEdit(self) - key_input.setObjectName("DictKey") - if self.labeled_items: def focused_out(event): QtWidgets.QLineEdit.focusOutEvent(key_input, event) - self._on_enter_press() + self._on_focus_lose() key_input.focusOutEvent = focused_out @@ -2034,7 +2034,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): if self.labeled_items: wrapper_widget.add_widget_after_label(key_input) wrapper_widget.add_widget_after_label(add_btn) - wrapper_widget.add_widget_after_label(edit_btn) + wrapper_widget.add_widget_before_label(edit_btn) else: layout.addWidget(add_btn, 0) @@ -2050,7 +2050,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): value_input.value_changed.connect(self._on_value_change) add_btn.clicked.connect(self.on_add_clicked) if edit_btn: - edit_btn.clicked.connect(self.on_edit_clicked) + edit_btn.clicked.connect(self.on_edit_pressed) if remove_btn: remove_btn.clicked.connect(self.on_remove_clicked) @@ -2092,6 +2092,14 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_edit_mode(True) self.update_style() + def _on_focus_lose(self): + if ( + self.key_input.hasFocus() + or self.edit_btn.hasFocus() + ): + return + self._on_enter_press() + def _on_enter_press(self): if not self.labeled_items: return @@ -2156,8 +2164,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_as_empty(False) self._parent.add_row(row=self.row() + 1, is_empty=True) - def on_edit_clicked(self): - self.set_edit_mode() + def on_edit_pressed(self): + if not self.key_input.isVisible(): + self.set_edit_mode() + else: + self.key_input.setFocus() def set_edit_mode(self, enabled=True): if self.is_invalid and not enabled: From 51f50cc5f4ef7e8411e137c1020ce1cfd0c27f7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 16:45:54 +0100 Subject: [PATCH 116/219] implemented IconButton that just change color on hover --- .../settings/settings/widgets/widgets.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index 6135f8dcb2..1ff5ce2fdc 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -1,4 +1,23 @@ from Qt import QtWidgets, QtCore, QtGui +from avalon.vendor import qtawesome + + +class IconButton(QtWidgets.QPushButton): + def __init__(self, icon_name, color, hover_color, *args, **kwargs): + super(IconButton, self).__init__(*args, **kwargs) + + self.icon = qtawesome.icon(icon_name, color=color) + self.hover_icon = qtawesome.icon(icon_name, color=hover_color) + + self.setIcon(self.icon) + + def enterEvent(self, event): + self.setIcon(self.hover_icon) + super(IconButton, self).enterEvent(event) + + def leaveEvent(self, event): + self.setIcon(self.icon) + super(IconButton, self).leaveEvent(event) class NumberSpinBox(QtWidgets.QDoubleSpinBox): From 012e62c370aa4d3595110bf3725688ddea09a195 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 16:46:17 +0100 Subject: [PATCH 117/219] used IconButton for edit --- pype/tools/settings/settings/style/style.css | 5 +++++ pype/tools/settings/settings/widgets/item_types.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index dcc7a5effe..38fcd14448 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -97,6 +97,11 @@ QPushButton[btn-type="tool-item"]:hover { background-color: transparent; } +QPushButton[btn-type="tool-item-icon"] { + border: 0px solid #bfccd6; + background-color: transparent; +} + QPushButton[btn-type="expand-toggle"] { background: #1d272f; } diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index cb7a310945..e00f21a1e2 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2,6 +2,7 @@ import json import collections from Qt import QtWidgets, QtCore, QtGui from .widgets import ( + IconButton, ExpandingWidget, NumberSpinBox, PathInput, @@ -2021,10 +2022,12 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): edit_btn = None remove_btn = None if self.labeled_items: - edit_btn = QtWidgets.QPushButton("Edit") + edit_btn = IconButton( + "fa.edit", QtCore.Qt.lightGray, QtCore.Qt.white + ) edit_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - edit_btn.setProperty("btn-type", "tool-item") - edit_btn.setFixedSize(self._btn_size, self._btn_size) + edit_btn.setProperty("btn-type", "tool-item-icon") + edit_btn.setFixedHeight(self._btn_size) else: remove_btn = QtWidgets.QPushButton("-") remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) From 8ceeabc21bd5a34997ce5aa6260c7bee46ddda5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:02:16 +0100 Subject: [PATCH 118/219] added label input --- .../settings/settings/widgets/item_types.py | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e00f21a1e2..5b92754752 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1988,8 +1988,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_input = QtWidgets.QLineEdit(self) key_input.setObjectName("DictKey") + key_label_input = None wrapper_widget = None if self.labeled_items: + key_label_input = QtWidgets.QLineEdit(self) + wrapper_widget = ExpandingWidget("", self) layout.addWidget(wrapper_widget) @@ -2002,11 +2005,16 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): content_layout.addWidget(value_input) - def focused_out(event): + def key_input_focused_out(event): QtWidgets.QLineEdit.focusOutEvent(key_input, event) self._on_focus_lose() - key_input.focusOutEvent = focused_out + def key_label_input_focused_out(event): + QtWidgets.QLineEdit.focusOutEvent(key_label_input, event) + self._on_focus_lose() + + key_input.focusOutEvent = key_input_focused_out + key_label_input.focusOutEvent = key_label_input_focused_out spacer_widget = None if not self.labeled_items: @@ -2035,9 +2043,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.setFixedSize(self._btn_size, self._btn_size) if self.labeled_items: - wrapper_widget.add_widget_after_label(key_input) - wrapper_widget.add_widget_after_label(add_btn) + wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) + wrapper_widget.add_widget_after_label(key_input) + wrapper_widget.add_widget_after_label(key_label_input) else: layout.addWidget(add_btn, 0) @@ -2050,7 +2059,12 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_input.textChanged.connect(self._on_key_change) key_input.returnPressed.connect(self._on_enter_press) + if key_label_input: + key_label_input.textChanged.connect(self._on_key_change) + key_label_input.returnPressed.connect(self._on_enter_press) + value_input.value_changed.connect(self._on_value_change) + add_btn.clicked.connect(self.on_add_clicked) if edit_btn: edit_btn.clicked.connect(self.on_edit_pressed) @@ -2058,6 +2072,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.clicked.connect(self.on_remove_clicked) self.key_input = key_input + self.key_label_input = key_label_input self.value_input = value_input self.wrapper_widget = wrapper_widget @@ -2091,14 +2106,18 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._is_key_duplicated = duplicated - if duplicated and self.labeled_items: - self.set_edit_mode(True) + if self.labeled_items: + if duplicated: + self.set_edit_mode(True) + else: + self._on_focus_lose() self.update_style() def _on_focus_lose(self): if ( - self.key_input.hasFocus() - or self.edit_btn.hasFocus() + self.edit_btn.hasFocus() + or self.key_input.hasFocus() + or self.key_label_input.hasFocus() ): return self._on_enter_press() @@ -2112,11 +2131,15 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): else: self.set_edit_mode(False) + def _on_key_label_change(self): + self.update_key_label() + def _on_key_change(self): - value = self.key_input.text() if self.value_is_env_group: - self.value_input.env_group_key = value - self.change_key_label(value) + self.value_input.env_group_key = self.key_input.text() + + self.update_key_label() + self._on_value_change() def _on_value_change(self, item=None): @@ -2146,9 +2169,16 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def is_group(self): return self._parent.is_group - def change_key_label(self, label): - if self.wrapper_widget: - self.wrapper_widget.label_widget.setText(label) + def update_key_label(self): + if not self.wrapper_widget: + return + key_value = self.key_input.text() + key_label_value = self.key_label_input.text() + if key_label_value: + label = "{} ({})".format(key_label_value, key_value) + else: + label = key_value + self.wrapper_widget.label_widget.setText(label) def on_add_clicked(self): if not self.labeled_items: @@ -2178,6 +2208,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self.wrapper_widget.label_widget.setVisible(not enabled) self.key_input.setVisible(enabled) + self.key_label_input.setVisible(enabled) if enabled: self.key_input.setFocus() @@ -2199,6 +2230,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): else: self.key_input.setVisible(is_empty) + self.key_label_input.setVisible(is_empty) self.edit_btn.setVisible(not is_empty) self.wrapper_widget.label_widget.setVisible(not is_empty) From 61ecb05a591f050366be770e4d6987c61d843322 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:10:26 +0100 Subject: [PATCH 119/219] adde remove btn on edit mode --- .../settings/settings/widgets/item_types.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 5b92754752..f76b9d2730 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2036,17 +2036,18 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): edit_btn.setFocusPolicy(QtCore.Qt.ClickFocus) edit_btn.setProperty("btn-type", "tool-item-icon") edit_btn.setFixedHeight(self._btn_size) - else: - remove_btn = QtWidgets.QPushButton("-") - remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - remove_btn.setProperty("btn-type", "tool-item") - remove_btn.setFixedSize(self._btn_size, self._btn_size) + + remove_btn = QtWidgets.QPushButton("-") + remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + remove_btn.setProperty("btn-type", "tool-item") + remove_btn.setFixedSize(self._btn_size, self._btn_size) if self.labeled_items: wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) wrapper_widget.add_widget_after_label(key_input) wrapper_widget.add_widget_after_label(key_label_input) + wrapper_widget.add_widget_after_label(remove_btn) else: layout.addWidget(add_btn, 0) @@ -2068,8 +2069,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): add_btn.clicked.connect(self.on_add_clicked) if edit_btn: edit_btn.clicked.connect(self.on_edit_pressed) - if remove_btn: - remove_btn.clicked.connect(self.on_remove_clicked) + remove_btn.clicked.connect(self.on_remove_clicked) self.key_input = key_input self.key_label_input = key_label_input @@ -2118,6 +2118,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.edit_btn.hasFocus() or self.key_input.hasFocus() or self.key_label_input.hasFocus() + or self.remove_btn.hasFocus() ): return self._on_enter_press() @@ -2209,13 +2210,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.wrapper_widget.label_widget.setVisible(not enabled) self.key_input.setVisible(enabled) self.key_label_input.setVisible(enabled) + self.remove_btn.setVisible(enabled) if enabled: self.key_input.setFocus() def on_remove_clicked(self): - self.remove_row() - - def remove_row(self): self._parent.remove_row(self) def set_as_empty(self, is_empty=True): @@ -2229,6 +2228,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.spacer_widget.setVisible(is_empty) else: + self.remove_btn.setVisible(False) self.key_input.setVisible(is_empty) self.key_label_input.setVisible(is_empty) self.edit_btn.setVisible(not is_empty) From a24f2a15343e31f43fc42eabb899cd3f1d14ca61 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:17:23 +0100 Subject: [PATCH 120/219] minor styles fixes --- pype/tools/settings/settings/widgets/item_types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index f76b9d2730..f2d42cdadf 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1997,6 +1997,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): layout.addWidget(wrapper_widget) content_widget = QtWidgets.QWidget(wrapper_widget) + content_widget.setObjectName("ContentWidget") content_layout = QtWidgets.QHBoxLayout(content_widget) content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) content_layout.setSpacing(5) @@ -2379,7 +2380,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): content_widget.setObjectName("ContentWidget") content_widget.setProperty("content_state", content_state) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, bottom_margin) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, bottom_margin) if body_widget is None: main_layout.addWidget(content_widget) From 5fb35c8f5b0bbcedb827b51bee2756b932356d69 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:33:50 +0100 Subject: [PATCH 121/219] fix layout --- pype/tools/settings/settings/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index 1ff5ce2fdc..66afa565a3 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -127,7 +127,7 @@ class ExpandingWidget(QtWidgets.QWidget): label_widget.setObjectName("DictLabel") before_label_widget = QtWidgets.QWidget(side_line_widget) - before_label_layout = QtWidgets.QVBoxLayout(before_label_widget) + before_label_layout = QtWidgets.QHBoxLayout(before_label_widget) before_label_layout.setContentsMargins(0, 0, 0, 0) after_label_widget = QtWidgets.QWidget(side_line_widget) From d49c683eec36e258e6a23cd9f59a3493bad56cd3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:35:27 +0100 Subject: [PATCH 122/219] added storing label value in metadata --- .../settings/settings/widgets/item_types.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index f2d42cdadf..ee4bbde5ca 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2281,6 +2281,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def row(self): return self._parent.input_fields.index(self) + def label_value(self): + return self.key_label_input.text() + def item_value(self): key = self.key_input.text() value = self.value_input.item_value() @@ -2503,12 +2506,29 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): output.update(item.item_value()) return output + def item_value_with_metadata(self): + output = {} + labels_by_key = {} + for item in self.input_fields: + labels_by_key[item.key_value()] = item.label_value() + output.update(item.config_value()) + + output[METADATA_KEY] = { + "dynamic_key_label": labels_by_key + } + return output + def item_value(self): output = {} for item in self.input_fields: output.update(item.config_value()) return output + def config_value(self): + if not self.labeled_items: + return super(ModifiableDict, self).config_value() + return {self.key: self.item_value_with_metadata()} + def add_row(self, row=None, key=None, value=None, is_empty=False): # Create new item item_widget = ModifiableDictItem( From 500df0a052e30b974ac003c5e75c6112efb5fd73 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:49:35 +0100 Subject: [PATCH 123/219] little bit safer metadata storing --- .../settings/settings/widgets/item_types.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 3ffba4893d..d2efb703f1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1342,10 +1342,10 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def config_value(self): value = self.item_value() if self.is_environ: - value[METADATA_KEY] = { - "environments": { - self.env_group_key: list(value.keys()) - } + if METADATA_KEY not in value: + value[METADATA_KEY] = {} + value[METADATA_KEY]["environments"] = { + self.env_group_key: list(value.keys()) } return {self.key: value} @@ -3617,7 +3617,9 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): if is_group: groups.extend(value.keys()) if groups: - values[METADATA_KEY] = {"groups": groups} + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups return values, self.is_group def overrides(self): @@ -3633,7 +3635,9 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): if is_group: groups.extend(value.keys()) if groups: - values[METADATA_KEY] = {"groups": groups} + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups return values, self.is_group From a4a684f32f5764a46598ed8530994892a94f3266 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:50:02 +0100 Subject: [PATCH 124/219] unified method for getting modifiable dict item value with metadata --- .../settings/settings/widgets/item_types.py | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d2efb703f1..db62beddf8 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2506,15 +2506,28 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): return output def item_value_with_metadata(self): - output = {} - labels_by_key = {} - for item in self.input_fields: - labels_by_key[item.key_value()] = item.label_value() - output.update(item.config_value()) + metadata = {} + if not self.labeled_items: + output = self.item_value() + + else: + output = {} + labels_by_key = {} + for item in self.input_fields: + labels_by_key[item.key_value()] = item.label_value() + output.update(item.config_value()) + metadata["dynamic_key_label"] = labels_by_key + + if self.value_is_env_group: + for key, value in tuple(output.items()): + metadata["environments"] = {key: list(value.keys())} + output[key] = value + + if metadata: + if METADATA_KEY not in output: + output[METADATA_KEY] = {} + output[METADATA_KEY].update(metadata) - output[METADATA_KEY] = { - "dynamic_key_label": labels_by_key - } return output def item_value(self): @@ -2524,16 +2537,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): return output def config_value(self): - output = self.item_value() - if self.value_is_env_group: - for key, value in tuple(output.items()): - value[METADATA_KEY] = { - "environments": { - key: list(value.keys()) - } - } - output[key] = value - return {self.key: output} + return {self.key: self.item_value_with_metadata()} def add_row(self, row=None, key=None, value=None, is_empty=False): # Create new item From b79d8b65ffc96e106fe35178c76f9e9616713fef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 17:55:10 +0100 Subject: [PATCH 125/219] fix anatomy creation --- pype/tools/settings/settings/widgets/anatomy_types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 680db140a6..83183e7943 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -67,6 +67,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget = RootsWidget(roots_input_data, self) self.templates_widget = TemplatesWidget(templates_input_data, self) self.imageio_widget = DictWidget(imageio_input_data, self) + self.imageio_widget.create_ui() self.setAttribute(QtCore.Qt.WA_StyledBackground) From 9de325987a1f7073ef51dc5d13c449711c8a351f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Nov 2020 17:57:58 +0100 Subject: [PATCH 126/219] Fix frame start, fix frame end --- .../stubs/aftereffects_server_stub.py | 2 +- .../plugins/aftereffects/publish/collect_render.py | 8 +++----- .../aftereffects/publish/extract_save_scene.py | 14 ++++++++++++++ .../publish/submit_aftereffects_deadline.py | 9 ++++----- pype/plugins/global/publish/submit_publish_job.py | 7 +++++++ 5 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 pype/plugins/aftereffects/publish/extract_save_scene.py diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index e47aaee471..0b8c54e884 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -321,7 +321,7 @@ class AfterEffectsServerStub(): if records: return records.pop() - log.debug("Couldn't get render queue info") + log.debug("Render queue needs to have file extension in 'Output to'") def get_audio_url(self, item_id): """ Get audio layer absolute url for comp diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index f053eb7ce3..fbe392d52b 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -40,13 +40,11 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): continue work_area_info = aftereffects.stub().get_work_area(int(item_id)) - frameStart = round(float(work_area_info.workAreaStart) * - float(work_area_info.frameRate)) + frameStart = work_area_info.workAreaStart - frameEnd = round(float(work_area_info.workAreaStart) * - float(work_area_info.frameRate) + + frameEnd = round(work_area_info.workAreaStart + float(work_area_info.workAreaDuration) * - float(work_area_info.frameRate)) + float(work_area_info.frameRate)) - 1 if inst["family"] == "render" and inst["active"]: instance = AERenderInstance( diff --git a/pype/plugins/aftereffects/publish/extract_save_scene.py b/pype/plugins/aftereffects/publish/extract_save_scene.py new file mode 100644 index 0000000000..e19065d086 --- /dev/null +++ b/pype/plugins/aftereffects/publish/extract_save_scene.py @@ -0,0 +1,14 @@ +import pype.api +from avalon import aftereffects + + +class ExtractSaveScene(pype.api.Extractor): + """Save scene before extraction.""" + + order = pype.api.Extractor.order - 0.49 + label = "Extract Save Scene" + hosts = ["aftereffects"] + families = ["workfile"] + + def process(self, instance): + aftereffects.stub().save() diff --git a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py index 8bb42a0239..d5f2747be7 100644 --- a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py +++ b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py @@ -29,6 +29,8 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline families = ["render.farm"] # cannot be "render' as that is integrated use_published = False + chunk_size = 1000000 + def get_job_info(self): dln_job_info = DeadlineJobInfo(Plugin="AfterEffects") @@ -45,8 +47,7 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline self._instance.data["frameEnd"]) dln_job_info.Frames = frame_range - if len(self._instance.data["expectedFiles"]) == 1: - dln_job_info.ChunkSize = 1000000 + dln_job_info.ChunkSize = self.chunk_size dln_job_info.OutputFilename = \ os.path.basename(self._instance.data["expectedFiles"][0]) @@ -97,10 +98,8 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline render_path = os.path.join(render_dir, '{}.{}.{}'.format(arr[0], hashed, arr[2])) - deadline_plugin_info.MultiProcess = True - else: - deadline_plugin_info.MultiProcess = False + deadline_plugin_info.MultiProcess = True deadline_plugin_info.Comp = self._instance.data["comp_name"] deadline_plugin_info.Version = "17.5" deadline_plugin_info.SceneFile = script_path diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 256bf01665..b8bf240c06 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -600,6 +600,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "files": os.path.basename(remainder), "stagingDir": os.path.dirname(remainder), } + if "render" in instance.get("families"): + rep.update({ + "fps": instance.get("fps"), + "tags": ["review"] + }) + self._solve_families(instance, True) + if remainder in bake_render_path: rep.update({ "fps": instance.get("fps"), From 3e23b9ab1b78f2c842505bcb3a7b09b9be2e535a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 18:12:57 +0100 Subject: [PATCH 127/219] fixed env group metadata storing --- .../settings/settings/widgets/item_types.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index db62beddf8..8bfb1dd091 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2506,7 +2506,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): return output def item_value_with_metadata(self): - metadata = {} if not self.labeled_items: output = self.item_value() @@ -2516,18 +2515,18 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): for item in self.input_fields: labels_by_key[item.key_value()] = item.label_value() output.update(item.config_value()) - metadata["dynamic_key_label"] = labels_by_key + if METADATA_KEY not in output: + output[METADATA_KEY] = {} + output[METADATA_KEY]["dynamic_key_label"] = labels_by_key if self.value_is_env_group: for key, value in tuple(output.items()): - metadata["environments"] = {key: list(value.keys())} + if METADATA_KEY not in value: + value[METADATA_KEY] = {} + value[METADATA_KEY]["environments"] = { + key: list(value.keys()) + } output[key] = value - - if metadata: - if METADATA_KEY not in output: - output[METADATA_KEY] = {} - output[METADATA_KEY].update(metadata) - return output def item_value(self): From 6903865976f404df6dbde84c13261cdaebb0eb38 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 18:34:26 +0100 Subject: [PATCH 128/219] fixed metadata --- .../settings/settings/widgets/item_types.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8bfb1dd091..5a02497f71 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1344,8 +1344,14 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): if self.is_environ: if METADATA_KEY not in value: value[METADATA_KEY] = {} + + env_keys = [] + for key in value.keys(): + if key is not METADATA_KEY: + env_keys.append(key) + value[METADATA_KEY]["environments"] = { - self.env_group_key: list(value.keys()) + self.env_group_key: env_keys } return {self.key: value} @@ -2520,13 +2526,17 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): output[METADATA_KEY]["dynamic_key_label"] = labels_by_key if self.value_is_env_group: - for key, value in tuple(output.items()): + for env_group_key, value in tuple(output.items()): + env_keys = [] + for key in value.keys(): + if key is not METADATA_KEY: + env_keys.append(key) + if METADATA_KEY not in value: value[METADATA_KEY] = {} - value[METADATA_KEY]["environments"] = { - key: list(value.keys()) - } - output[key] = value + + value[METADATA_KEY]["environments"] = {env_group_key: env_keys} + output[env_group_key] = value return output def item_value(self): From 0b18f1cce3238665bc7f97ffd8de550eed23ac90 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:01:25 +0100 Subject: [PATCH 129/219] fixed few issues of loaded data --- pype/settings/lib.py | 2 ++ pype/tools/settings/settings/widgets/item_types.py | 6 +++++- pype/tools/settings/settings/widgets/lib.py | 13 ++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index a50d7793b5..ebd0d86df4 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -9,6 +9,8 @@ log = logging.getLogger(__name__) M_OVERRIDEN_KEY = "__overriden_keys__" # Metadata key for storing information about environments M_ENVIRONMENT_KEY = "__environment_keys__" +# Metadata key for storing dynamic created labels +M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__" # NOTE key popping not implemented yet M_POP_KEY = "__pop_key__" diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 5a02497f71..ca4d28ccbc 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2034,7 +2034,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): add_btn.setFixedSize(self._btn_size, self._btn_size) edit_btn = None - remove_btn = None if self.labeled_items: edit_btn = IconButton( "fa.edit", QtCore.Qt.lightGray, QtCore.Qt.white @@ -2049,6 +2048,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.setFixedSize(self._btn_size, self._btn_size) if self.labeled_items: + add_btn.setVisible(False) + remove_btn.setVisible(False) + wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) wrapper_widget.add_widget_after_label(key_input) @@ -2422,6 +2424,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): previous_inputs = tuple(self.input_fields) for item_key, item_value in value.items(): self.add_row(key=item_key, value=item_value) + if self.labeled_items: + self.add_row(is_empty=True) for input_field in previous_inputs: self.remove_row(input_field) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index f112a6e975..9a6331009b 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -2,7 +2,11 @@ import os import re import json import copy -from pype.settings.lib import M_OVERRIDEN_KEY, M_ENVIRONMENT_KEY +from pype.settings.lib import ( + M_OVERRIDEN_KEY, + M_ENVIRONMENT_KEY, + M_DYNAMIC_KEY_LABEL +) from queue import Queue @@ -35,6 +39,8 @@ def convert_gui_data_with_metadata(data, ignored_keys=None): if key == "environments": output[M_ENVIRONMENT_KEY] = value + elif key == "dynamic_key_label": + output[M_DYNAMIC_KEY_LABEL] = value else: raise KeyError("Unknown metadata key \"{}\"".format(key)) @@ -51,6 +57,11 @@ def convert_data_to_gui_data(data, first=True): if M_ENVIRONMENT_KEY in data: data.pop(M_ENVIRONMENT_KEY) + if M_DYNAMIC_KEY_LABEL in data: + if METADATA_KEY not in data: + data[METADATA_KEY] = {} + data[METADATA_KEY]["dynamic_key_label"] = data.pop(M_DYNAMIC_KEY_LABEL) + for key, value in data.items(): output[key] = convert_data_to_gui_data(value, False) From 295f89a730deada88fce45bf8ad37b9f2ec0daeb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:02:56 +0100 Subject: [PATCH 130/219] add_row can enter label --- .../settings/settings/widgets/item_types.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ca4d28ccbc..11c8fdeaa9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2155,19 +2155,25 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.update_style() self.value_changed.emit(self) - def update_default_values(self, key, value): + def update_default_values(self, key, label, value): self.origin_key = key self.key_input.setText(key) + if self.key_label_input: + self.key_label_input.setText(label or "") self.value_input.update_default_values(value) - def update_studio_values(self, key, value): + def update_studio_values(self, key, label, value): self.origin_key = key self.key_input.setText(key) + if self.key_label_input: + self.key_label_input.setText(label or "") self.value_input.update_studio_values(value) - def apply_overrides(self, key, value): + def apply_overrides(self, key, label, value): self.origin_key = key self.key_input.setText(key) + if self.key_label_input: + self.key_label_input.setText(label or "") self.value_input.apply_overrides(value) @property @@ -2421,9 +2427,14 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def set_value(self, value): self.validate_value(value) + metadata = value.pop(METADATA_KEY, {}) + dynamic_key_labels = metadata.get("dynamic_key_label") or {} + previous_inputs = tuple(self.input_fields) for item_key, item_value in value.items(): - self.add_row(key=item_key, value=item_value) + label = dynamic_key_labels.get(item_key) + self.add_row(key=item_key, label=label, value=item_value) + if self.labeled_items: self.add_row(is_empty=True) @@ -2552,7 +2563,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def config_value(self): return {self.key: self.item_value_with_metadata()} - def add_row(self, row=None, key=None, value=None, is_empty=False): + def add_row( + self, row=None, key=None, label=None, value=None, is_empty=False + ): # Create new item item_widget = ModifiableDictItem( self.item_schema, self, self.content_widget @@ -2593,11 +2606,11 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: if not self._has_studio_override: - item_widget.update_default_values(key, value) + item_widget.update_default_values(key, label, value) elif self._is_overriden: - item_widget.apply_overrides(key, value) + item_widget.apply_overrides(key, label, value) else: - item_widget.update_studio_values(key, value) + item_widget.update_studio_values(key, label, value) self.hierarchical_style_update() else: self._on_value_change() From 4bceafe3e569bab0820702e15e35be62f0de1d6a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:04:50 +0100 Subject: [PATCH 131/219] better initial state of modifiable dict item --- pype/tools/settings/settings/widgets/item_types.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 11c8fdeaa9..1c70b67bdb 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2048,9 +2048,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.setFixedSize(self._btn_size, self._btn_size) if self.labeled_items: - add_btn.setVisible(False) - remove_btn.setVisible(False) - wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) wrapper_widget.add_widget_after_label(key_input) @@ -2091,6 +2088,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.edit_btn = edit_btn self.remove_btn = remove_btn + self.set_as_empty(self._is_empty) + @property def labeled_items(self): return self._parent.labeled_items From dc353e03a805fcdf253b24fc2bb1f3dc76b1cfc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:08:57 +0100 Subject: [PATCH 132/219] do not pop metadata key, just skip it --- pype/tools/settings/settings/widgets/item_types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1c70b67bdb..faaf9de4bb 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2426,11 +2426,12 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def set_value(self, value): self.validate_value(value) - metadata = value.pop(METADATA_KEY, {}) + metadata = value.get(METADATA_KEY, {}) dynamic_key_labels = metadata.get("dynamic_key_label") or {} - previous_inputs = tuple(self.input_fields) for item_key, item_value in value.items(): + if item_key is METADATA_KEY: + continue label = dynamic_key_labels.get(item_key) self.add_row(key=item_key, label=label, value=item_value) From 6cd6d9ef9036adb9aa0dab1fa043193432eb9f5d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:40:10 +0100 Subject: [PATCH 133/219] proper stylesheets and key label modifications --- .../settings/settings/widgets/item_types.py | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index faaf9de4bb..1eadd843a3 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1973,6 +1973,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._is_key_duplicated = False self.origin_key = NOT_SET + self.origin_key_label = NOT_SET if self.labeled_items: layout = QtWidgets.QVBoxLayout(self) @@ -2090,6 +2091,23 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_as_empty(self._is_empty) + def _style_state(self): + if self.as_widget: + state = self.style_state( + False, + self._is_invalid, + False, + self.is_modified + ) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) + return state + @property def labeled_items(self): return self._parent.labeled_items @@ -2158,21 +2176,27 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.origin_key = key self.key_input.setText(key) if self.key_label_input: - self.key_label_input.setText(label or "") + label = label or "" + self.origin_key_label = label + self.key_label_input.setText(label) self.value_input.update_default_values(value) def update_studio_values(self, key, label, value): self.origin_key = key self.key_input.setText(key) if self.key_label_input: - self.key_label_input.setText(label or "") + label = label or "" + self.origin_key_label = label + self.key_label_input.setText(label) self.value_input.update_studio_values(value) def apply_overrides(self, key, label, value): self.origin_key = key self.key_input.setText(key) if self.key_label_input: - self.key_label_input.setText(label or "") + label = label or "" + self.origin_key_label = label + self.key_label_input.setText(label) self.value_input.apply_overrides(value) @property @@ -2260,6 +2284,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def is_key_modified(self): return self.key_value() != self.origin_key + def is_key_label_modified(self): + return self.key_label_value() != self.origin_key_label + def is_value_modified(self): return self.value_input.is_modified @@ -2267,7 +2294,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def is_modified(self): if self._is_empty: return False - return self.is_value_modified() or self.is_key_modified() + return ( + self.is_value_modified() + or self.is_key_modified() + or self.is_key_label_modified() + ) def hierarchical_style_update(self): self.value_input.hierarchical_style_update() @@ -2280,21 +2311,49 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self.is_key_invalid() or self.value_input.is_invalid def update_style(self): - state = "" + key_input_state = "" if not self._is_empty: if self.is_key_invalid(): - state = "invalid" + key_input_state = "invalid" elif self.is_key_modified(): - state = "modified" + key_input_state = "modified" - self.key_input.setProperty("state", state) + self.key_input.setProperty("state", key_input_state) self.key_input.style().polish(self.key_input) + if not self.wrapper_widget: + return + + state = self._style_state() + + if self._state == state: + return + + self._state = state + + if self.wrapper_widget.label_widget: + self.wrapper_widget.label_widget.setProperty("state", state) + self.wrapper_widget.label_widget.style().polish( + self.wrapper_widget.label_widget + ) + + if state: + child_state = "child-{}".format(state) + else: + child_state = "" + + self.wrapper_widget.side_line_widget.setProperty("state", child_state) + self.wrapper_widget.side_line_widget.style().polish( + self.wrapper_widget.side_line_widget + ) + def row(self): return self._parent.input_fields.index(self) - def label_value(self): - return self.key_label_input.text() + def key_label_value(self): + if self.labeled_items: + return self.key_label_input.text() + return NOT_SET def item_value(self): key = self.key_input.text() @@ -2534,7 +2593,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): output = {} labels_by_key = {} for item in self.input_fields: - labels_by_key[item.key_value()] = item.label_value() + labels_by_key[item.key_value()] = item.key_label_value() output.update(item.config_value()) if METADATA_KEY not in output: output[METADATA_KEY] = {} From ccc758cdced307c7eaa134099fca74e5e94a78ea Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 27 Nov 2020 19:44:22 +0100 Subject: [PATCH 134/219] added labels to inputs --- pype/tools/settings/settings/widgets/item_types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1eadd843a3..d4942e04d2 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2048,10 +2048,16 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.setProperty("btn-type", "tool-item") remove_btn.setFixedSize(self._btn_size, self._btn_size) + key_input_label_widget = None + key_label_input_label_widget = None if self.labeled_items: + key_input_label_widget = QtWidgets.QLabel("Key:") + key_label_input_label_widget = QtWidgets.QLabel("Label:") wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) + wrapper_widget.add_widget_after_label(key_input_label_widget) wrapper_widget.add_widget_after_label(key_input) + wrapper_widget.add_widget_after_label(key_label_input_label_widget) wrapper_widget.add_widget_after_label(key_label_input) wrapper_widget.add_widget_after_label(remove_btn) @@ -2078,9 +2084,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): remove_btn.clicked.connect(self.on_remove_clicked) self.key_input = key_input + self.key_input_label_widget = key_input_label_widget self.key_label_input = key_label_input + self.key_label_input_label_widget = key_label_input_label_widget self.value_input = value_input - self.wrapper_widget = wrapper_widget self.spacer_widget = spacer_widget @@ -2245,7 +2252,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): if self.is_invalid and not enabled: return self.wrapper_widget.label_widget.setVisible(not enabled) + self.key_label_input_label_widget.setVisible(enabled) self.key_input.setVisible(enabled) + self.key_input_label_widget.setVisible(enabled) self.key_label_input.setVisible(enabled) self.remove_btn.setVisible(enabled) if enabled: @@ -2266,7 +2275,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): else: self.remove_btn.setVisible(False) + self.key_input_label_widget.setVisible(is_empty) self.key_input.setVisible(is_empty) + self.key_label_input_label_widget.setVisible(is_empty) self.key_label_input.setVisible(is_empty) self.edit_btn.setVisible(not is_empty) From 6e9fc1c13e6760a39bcc445b1be5fa64b52a241f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Nov 2020 20:37:29 +0100 Subject: [PATCH 135/219] Fix activate 'use_published' --- pype/lib/abstract_submit_deadline.py | 4 +++- .../aftereffects/publish/submit_aftereffects_deadline.py | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pype/lib/abstract_submit_deadline.py b/pype/lib/abstract_submit_deadline.py index 09916523a4..170e4908b7 100644 --- a/pype/lib/abstract_submit_deadline.py +++ b/pype/lib/abstract_submit_deadline.py @@ -520,6 +520,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): f.replace(orig_scene, new_scene) ) new_exp[aov] = replaced_files + # [] might be too much here, TODO self._instance.data["expectedFiles"] = [new_exp] else: new_exp = [] @@ -527,7 +528,8 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): new_exp.append( f.replace(orig_scene, new_scene) ) - self._instance.data["expectedFiles"] = [new_exp] + self._instance.data["expectedFiles"] = new_exp + self.log.info("Scene name was switched {} -> {}".format( orig_scene, new_scene )) diff --git a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py index d5f2747be7..9414bdd39d 100644 --- a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py +++ b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py @@ -24,10 +24,10 @@ class DeadlinePluginInfo(): class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): label = "Submit AE to Deadline" - order = pyblish.api.IntegratorOrder + order = pyblish.api.IntegratorOrder + 0.1 hosts = ["aftereffects"] families = ["render.farm"] # cannot be "render' as that is integrated - use_published = False + use_published = True chunk_size = 1000000 @@ -48,7 +48,6 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline dln_job_info.Frames = frame_range dln_job_info.ChunkSize = self.chunk_size - dln_job_info.OutputFilename = \ os.path.basename(self._instance.data["expectedFiles"][0]) dln_job_info.OutputDirectory = \ @@ -102,7 +101,7 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline deadline_plugin_info.MultiProcess = True deadline_plugin_info.Comp = self._instance.data["comp_name"] deadline_plugin_info.Version = "17.5" - deadline_plugin_info.SceneFile = script_path + deadline_plugin_info.SceneFile = self.scene_path deadline_plugin_info.Output = render_path.replace("\\", "/") return attr.asdict(deadline_plugin_info) From 019562e5ace866850d6299041e2ef88e0fda2dad Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 27 Nov 2020 21:41:49 +0100 Subject: [PATCH 136/219] integrate hierarchy ftrack temporary python 2 fix --- .../publish/integrate_hierarchy_ftrack.py | 51 +-- .../publish/integrate_hierarchy_ftrack_SP.py | 331 ++++++++++++++++++ 2 files changed, 341 insertions(+), 41 deletions(-) create mode 100644 pype/plugins/ftrack/publish/integrate_hierarchy_ftrack_SP.py diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index ef8ee9a216..a1377cc771 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -1,13 +1,12 @@ import sys import six -import collections import pyblish.api from avalon import io -from pype.modules.ftrack.lib.avalon_sync import ( - CUST_ATTR_AUTO_SYNC, - get_pype_attr -) +try: + from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_AUTO_SYNC +except Exception: + CUST_ATTR_AUTO_SYNC = "avalon_auto_sync" class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): @@ -37,6 +36,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): order = pyblish.api.IntegratorOrder - 0.04 label = 'Integrate Hierarchy To Ftrack' families = ["shot"] + hosts = ["hiero"] optional = False def process(self, context): @@ -74,15 +74,6 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): self.auto_sync_on(project) def import_to_ftrack(self, input_data, parent=None): - # Prequery hiearchical custom attributes - hier_custom_attributes = get_pype_attr(self.session)[1] - hier_attr_by_key = { - attr["key"]: attr - for attr in hier_custom_attributes - } - # Get ftrack api module (as they are different per python version) - ftrack_api = self.context.data["ftrackPythonModule"] - for entity_name in input_data: entity_data = input_data[entity_name] entity_type = entity_data['entity_type'] @@ -125,34 +116,12 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): i for i in self.context if i.data['asset'] in entity['name'] ] for key in custom_attributes: - hier_attr = hier_attr_by_key.get(key) - # Use simple method if key is not hierarchical - if not hier_attr: - assert (key in entity['custom_attributes']), ( - 'Missing custom attribute key: `{0}` in attrs: ' - '`{1}`'.format(key, entity['custom_attributes'].keys()) - ) + assert (key in entity['custom_attributes']), ( + 'Missing custom attribute key: `{0}` in attrs: ' + '`{1}`'.format(key, entity['custom_attributes'].keys()) + ) - entity['custom_attributes'][key] = custom_attributes[key] - - else: - # Use ftrack operations method to set hiearchical - # attribute value. - # - this is because there may be non hiearchical custom - # attributes with different properties - entity_key = collections.OrderedDict({ - "configuration_id": hier_attr["id"], - "entity_id": entity["id"] - }) - self.session.recorded_operations.push( - ftrack_api.operation.UpdateEntityOperation( - "ContextCustomAttributeValue", - entity_key, - "value", - ftrack_api.symbol.NOT_SET, - custom_attributes[key] - ) - ) + entity['custom_attributes'][key] = custom_attributes[key] for instance in instances: instance.data['ftrackEntity'] = entity diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack_SP.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack_SP.py new file mode 100644 index 0000000000..ac606ed27d --- /dev/null +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack_SP.py @@ -0,0 +1,331 @@ +import sys +import six +import collections +import pyblish.api +from avalon import io + +from pype.modules.ftrack.lib.avalon_sync import ( + CUST_ATTR_AUTO_SYNC, + get_pype_attr +) + + +class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): + """ + Create entities in ftrack based on collected data from premiere + Example of entry data: + { + "ProjectXS": { + "entity_type": "Project", + "custom_attributes": { + "fps": 24,... + }, + "tasks": [ + "Compositing", + "Lighting",... *task must exist as task type in project schema* + ], + "childs": { + "sq01": { + "entity_type": "Sequence", + ... + } + } + } + } + """ + + order = pyblish.api.IntegratorOrder - 0.04 + label = 'Integrate Hierarchy To Ftrack' + families = ["shot"] + hosts = ["standalonepublisher"] + optional = False + + def process(self, context): + self.context = context + if "hierarchyContext" not in self.context.data: + return + + hierarchy_context = self.context.data["hierarchyContext"] + + self.session = self.context.data["ftrackSession"] + project_name = self.context.data["projectEntity"]["name"] + query = 'Project where full_name is "{}"'.format(project_name) + project = self.session.query(query).one() + auto_sync_state = project[ + "custom_attributes"][CUST_ATTR_AUTO_SYNC] + + if not io.Session: + io.install() + + self.ft_project = None + + input_data = hierarchy_context + + # disable termporarily ftrack project's autosyncing + if auto_sync_state: + self.auto_sync_off(project) + + try: + # import ftrack hierarchy + self.import_to_ftrack(input_data) + except Exception: + raise + finally: + if auto_sync_state: + self.auto_sync_on(project) + + def import_to_ftrack(self, input_data, parent=None): + # Prequery hiearchical custom attributes + hier_custom_attributes = get_pype_attr(self.session)[1] + hier_attr_by_key = { + attr["key"]: attr + for attr in hier_custom_attributes + } + # Get ftrack api module (as they are different per python version) + ftrack_api = self.context.data["ftrackPythonModule"] + + for entity_name in input_data: + entity_data = input_data[entity_name] + entity_type = entity_data['entity_type'] + self.log.debug(entity_data) + self.log.debug(entity_type) + + if entity_type.lower() == 'project': + query = 'Project where full_name is "{}"'.format(entity_name) + entity = self.session.query(query).one() + self.ft_project = entity + self.task_types = self.get_all_task_types(entity) + + elif self.ft_project is None or parent is None: + raise AssertionError( + "Collected items are not in right order!" + ) + + # try to find if entity already exists + else: + query = ( + 'TypedContext where name is "{0}" and ' + 'project_id is "{1}"' + ).format(entity_name, self.ft_project["id"]) + try: + entity = self.session.query(query).one() + except Exception: + entity = None + + # Create entity if not exists + if entity is None: + entity = self.create_entity( + name=entity_name, + type=entity_type, + parent=parent + ) + # self.log.info('entity: {}'.format(dict(entity))) + # CUSTOM ATTRIBUTES + custom_attributes = entity_data.get('custom_attributes', []) + instances = [ + i for i in self.context if i.data['asset'] in entity['name'] + ] + for key in custom_attributes: + hier_attr = hier_attr_by_key.get(key) + # Use simple method if key is not hierarchical + if not hier_attr: + assert (key in entity['custom_attributes']), ( + 'Missing custom attribute key: `{0}` in attrs: ' + '`{1}`'.format(key, entity['custom_attributes'].keys()) + ) + + entity['custom_attributes'][key] = custom_attributes[key] + + else: + # Use ftrack operations method to set hiearchical + # attribute value. + # - this is because there may be non hiearchical custom + # attributes with different properties + entity_key = collections.OrderedDict({ + "configuration_id": hier_attr["id"], + "entity_id": entity["id"] + }) + self.session.recorded_operations.push( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + entity_key, + "value", + ftrack_api.symbol.NOT_SET, + custom_attributes[key] + ) + ) + + for instance in instances: + instance.data['ftrackEntity'] = entity + + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + # TASKS + tasks = entity_data.get('tasks', []) + existing_tasks = [] + tasks_to_create = [] + for child in entity['children']: + if child.entity_type.lower() == 'task': + existing_tasks.append(child['name'].lower()) + # existing_tasks.append(child['type']['name']) + + for task_name in tasks: + task_type = tasks[task_name]["type"] + if task_name.lower() in existing_tasks: + print("Task {} already exists".format(task_name)) + continue + tasks_to_create.append((task_name, task_type)) + + for task_name, task_type in tasks_to_create: + self.create_task( + name=task_name, + task_type=task_type, + parent=entity + ) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + # Incoming links. + self.create_links(entity_data, entity) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + # Create notes. + user = self.session.query( + "User where username is \"{}\"".format(self.session.api_user) + ).first() + if user: + for comment in entity_data.get("comments", []): + entity.create_note(comment, user) + else: + self.log.warning( + "Was not able to query current User {}".format( + self.session.api_user + ) + ) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + # Import children. + if 'childs' in entity_data: + self.import_to_ftrack( + entity_data['childs'], entity) + + def create_links(self, entity_data, entity): + # Clear existing links. + for link in entity.get("incoming_links", []): + self.session.delete(link) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + # Create new links. + for input in entity_data.get("inputs", []): + input_id = io.find_one({"_id": input})["data"]["ftrackId"] + assetbuild = self.session.get("AssetBuild", input_id) + self.log.debug( + "Creating link from {0} to {1}".format( + assetbuild["name"], entity["name"] + ) + ) + self.session.create( + "TypedContextLink", {"from": assetbuild, "to": entity} + ) + + def get_all_task_types(self, project): + tasks = {} + proj_template = project['project_schema'] + temp_task_types = proj_template['_task_type_schema']['types'] + + for type in temp_task_types: + if type['name'] not in tasks: + tasks[type['name']] = type + + return tasks + + def create_task(self, name, task_type, parent): + task = self.session.create('Task', { + 'name': name, + 'parent': parent + }) + # TODO not secured!!! - check if task_type exists + self.log.info(task_type) + self.log.info(self.task_types) + task['type'] = self.task_types[task_type] + + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + return task + + def create_entity(self, name, type, parent): + entity = self.session.create(type, { + 'name': name, + 'parent': parent + }) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + return entity + + def auto_sync_off(self, project): + project["custom_attributes"][CUST_ATTR_AUTO_SYNC] = False + + self.log.info("Ftrack autosync swithed off") + + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) + + def auto_sync_on(self, project): + + project["custom_attributes"][CUST_ATTR_AUTO_SYNC] = True + + self.log.info("Ftrack autosync swithed on") + + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + self.session._configure_locations() + six.reraise(tp, value, tb) From 98d9f5e5c9ae5acb47e84914ff39fa969f4b6d80 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 27 Nov 2020 21:42:56 +0100 Subject: [PATCH 137/219] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index d0979fd030..e199aa5550 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.14.0" +__version__ = "2.14.1" From 6a4c768f21182ed2e0a0584311dc4ca3cc6b4b98 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:06:48 +0100 Subject: [PATCH 138/219] there are not added nuke arguments in prelaunch hooks --- pype/hooks/hiero/pre_launch_args.py | 14 ++------------ pype/hooks/maya/pre_launch_args.py | 3 ++- pype/hooks/nukestudio/pre_launch_args.py | 14 ++------------ pype/hooks/nukex/pre_launch_args.py | 12 +----------- 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index cb03f03b88..7139f57bbb 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -7,18 +7,8 @@ class HieroLaunchArguments(PreLaunchHook): hosts = ["hiero"] def execute(self): - """Prepare suprocess launch arguments for NukeX.""" - # Get executable - executable = self.launch_context.launch_args[0] - - if isinstance(executable, str): - executable = [executable] - - # Add `nukex` argument and make sure it's bind to execuable - executable.append("--hiero") - - self.launch_context.launch_args[0] = executable - + """Prepare suprocess launch arguments for Hiero.""" + # Add path to workfile to arguments if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py index 6aed54a3c3..570c8e79b8 100644 --- a/pype/hooks/maya/pre_launch_args.py +++ b/pype/hooks/maya/pre_launch_args.py @@ -8,7 +8,8 @@ class MayaLaunchArguments(PreLaunchHook): hosts = ["maya"] def execute(self): - """Prepare suprocess launch arguments for NukeX.""" + """Prepare suprocess launch arguments for Maya.""" + # Add path to workfile to arguments if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index d567f36ad0..a5e04bf956 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -7,18 +7,8 @@ class NukeStudioLaunchArguments(PreLaunchHook): hosts = ["nukestudio"] def execute(self): - """Prepare suprocess launch arguments for NukeX.""" - # Get executable - executable = self.launch_context.launch_args[0] - - if isinstance(executable, str): - executable = [executable] - - # Add `nukex` argument and make sure it's bind to execuable - executable.append("--studio") - - self.launch_context.launch_args[0] = executable - + """Prepare suprocess launch arguments for NukeStudio.""" + # Add path to workfile to arguments if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index eb0b963926..39ccb5a58a 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -8,17 +8,7 @@ class NukeXLaunchArguments(PreLaunchHook): def execute(self): """Prepare suprocess launch arguments for NukeX.""" - # Get executable - executable = self.launch_context.launch_args[0] - - if isinstance(executable, str): - executable = [executable] - - # Add `nukex` argument and make sure it's bind to execuable - executable.append("--nukex") - - self.launch_context.launch_args[0] = executable - + # Add path to workfile to arguments if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): From cf00c0c57729aca8328a9216a37fea718eb4fcff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:06:56 +0100 Subject: [PATCH 139/219] added resolve prelaunch hook --- .../celaction/pre_celaction_registers.py | 3 +- pype/hooks/resolve/pre_resolve_setup.py | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 pype/hooks/resolve/pre_resolve_setup.py diff --git a/pype/hooks/celaction/pre_celaction_registers.py b/pype/hooks/celaction/pre_celaction_registers.py index b65deda7f5..f592026699 100644 --- a/pype/hooks/celaction/pre_celaction_registers.py +++ b/pype/hooks/celaction/pre_celaction_registers.py @@ -41,8 +41,7 @@ class CelactionPrelaunchHook(PreLaunchHook): # TODO: change to root path and pyblish standalone to premiere way pype_root_path = os.getenv("PYPE_SETUP_PATH") - path = os.path.join(pype_root_path, - "pype.bat") + path = os.path.join(pype_root_path, "pype.bat") winreg.SetValueEx(hKey, "SubmitAppTitle", 0, winreg.REG_SZ, path) diff --git a/pype/hooks/resolve/pre_resolve_setup.py b/pype/hooks/resolve/pre_resolve_setup.py new file mode 100644 index 0000000000..336fabf0c4 --- /dev/null +++ b/pype/hooks/resolve/pre_resolve_setup.py @@ -0,0 +1,58 @@ +import os +import importlib +from pype.lib import PreLaunchHook +from pype.hosts.resolve import utils + + +class ResolvePrelaunch(PreLaunchHook): + """ + This hook will check if current workfile path has Resolve + project inside. IF not, it initialize it and finally it pass + path to the project by environment variable to Premiere launcher + shell script. + """ + hosts = ["resolve"] + + def execute(self): + # making sure pyton 3.6 is installed at provided path + py36_dir = os.path.normpath(self.env.get("PYTHON36_RESOLVE", "")) + assert os.path.isdir(py36_dir), ( + "Python 3.6 is not installed at the provided folder path. Either " + "make sure the `environments\resolve.json` is having correctly " + "set `PYTHON36_RESOLVE` or make sure Python 3.6 is installed " + f"in given path. \nPYTHON36_RESOLVE: `{py36_dir}`" + ) + self.log.info(f"Path to Resolve Python folder: `{py36_dir}`...") + self.env["PYTHON36_RESOLVE"] = py36_dir + + # setting utility scripts dir for scripts syncing + us_dir = os.path.normpath( + self.env.get("RESOLVE_UTILITY_SCRIPTS_DIR", "") + ) + assert os.path.isdir(us_dir), ( + "Resolve utility script dir does not exists. Either make sure " + "the `environments\resolve.json` is having correctly set " + "`RESOLVE_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" + f"RESOLVE_UTILITY_SCRIPTS_DIR: `{us_dir}`" + ) + self.log.debug(f"-- us_dir: `{us_dir}`") + + # correctly format path for pre python script + pre_py_sc = os.path.normpath(self.env.get("PRE_PYTHON_SCRIPT", "")) + self.env["PRE_PYTHON_SCRIPT"] = pre_py_sc + self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") + try: + __import__("pype.hosts.resolve") + __import__("pyblish") + + except ImportError: + self.log.warning( + "pyblish: Could not load Resolve integration.", + exc_info=True + ) + + else: + # Resolve Setup integration + importlib.reload(utils) + self.log.debug(f"-- utils.__file__: `{utils.__file__}`") + utils.setup(self.env) From 819998f068989614839f629ee4dfecf47aa37ce3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:34:11 +0100 Subject: [PATCH 140/219] replaced `is_host` key with `host_name` --- .../system_settings/global/applications.json | 34 +++++++++---------- .../template_host_unchangables.json | 22 ++++++++++-- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index c638f1e3d2..7dd0057110 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -3,7 +3,7 @@ "enabled": true, "label": "Autodesk Maya", "icon": "{}/app_icons/maya.png", - "is_host": true, + "host_name": "maya", "environment": { "__environment_keys__": { "maya": [ @@ -107,7 +107,7 @@ "enabled": true, "label": "Autodesk MayaBatch", "icon": "{}/app_icons/maya.png", - "is_host": false, + "host_name": "maya", "environment": { "__environment_keys__": { "mayabatch": [ @@ -205,7 +205,7 @@ "enabled": true, "label": "Nuke", "icon": "{}/app_icons/nuke.png", - "is_host": true, + "host_name": "nuke", "environment": { "__environment_keys__": { "nuke": [ @@ -287,7 +287,7 @@ "enabled": true, "label": "Nuke X", "icon": "{}/app_icons/nuke.png", - "is_host": true, + "host_name": "nuke", "environment": { "__environment_keys__": { "nukex": [ @@ -369,7 +369,7 @@ "enabled": true, "label": "Nuke Studio", "icon": "{}/app_icons/nuke.png", - "is_host": true, + "host_name": "hiero", "environment": { "__environment_keys__": { "nukestudio": [ @@ -453,7 +453,7 @@ "enabled": true, "label": "Hiero", "icon": "{}/app_icons/hiero.png", - "is_host": true, + "host_name": "hiero", "environment": { "__environment_keys__": { "hiero": [ @@ -539,7 +539,7 @@ "enabled": true, "label": "BlackMagic Fusion", "icon": "{}/app_icons/fusion.png", - "is_host": true, + "host_name": "fusion", "environment": { "__environment_keys__": { "fusion": [] @@ -584,7 +584,7 @@ "enabled": true, "label": "Blackmagic DaVinci Resolve", "icon": "{}/app_icons/resolve.png", - "is_host": true, + "host_name": "resolve", "environment": { "__environment_keys__": { "resolve": [ @@ -662,7 +662,7 @@ "enabled": true, "label": "SideFX Houdini", "icon": "{}/app_icons/houdini.png", - "is_host": true, + "host_name": "houdini", "environment": { "__environment_keys__": { "houdini": [ @@ -720,7 +720,7 @@ "enabled": true, "label": "Blender", "icon": "{}/app_icons/blender.png", - "is_host": true, + "host_name": "blender", "environment": { "__environment_keys__": { "blender": [ @@ -775,7 +775,7 @@ "enabled": true, "label": "Toon Boom Harmony", "icon": "{}/app_icons/harmony.png", - "is_host": true, + "host_name": "harmony", "environment": { "__environment_keys__": { "harmony": [ @@ -859,7 +859,7 @@ "enabled": true, "label": "TVPaint", "icon": "{}/app_icons/tvpaint.png", - "is_host": true, + "host_name": "tvpaint", "environment": { "__environment_keys__": { "tvpaint": [ @@ -911,7 +911,7 @@ "enabled": true, "label": "Adobe Photoshop", "icon": "{}/app_icons/photoshop.png", - "is_host": true, + "host_name": "photoshop", "environment": { "__environment_keys__": { "photoshop": [ @@ -971,7 +971,7 @@ "enabled": true, "label": "Adobe AfterEffects", "icon": "{}/app_icons/aftereffects.png", - "is_host": true, + "host_name": "aftereffects", "environment": { "__environment_keys__": { "aftereffects": [ @@ -1031,7 +1031,7 @@ "enabled": true, "label": "CelAction 2D", "icon": "app_icons/celaction.png", - "is_host": true, + "host_name": "celaction", "environment": { "__environment_keys__": { "celaction": [ @@ -1071,7 +1071,7 @@ "enabled": true, "label": "Unreal Editor", "icon": "{}/app_icons/ue4.png'", - "is_host": true, + "host_name": "unreal", "environment": { "__environment_keys__": { "unreal": [ @@ -1165,7 +1165,7 @@ "enabled": true, "label": "DJV View", "icon": "{}/app_icons/djvView.png", - "is_host": false, + "host_name": "", "environment": { "__environment_keys__": { "djvview": [] diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_unchangables.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_unchangables.json index 732fd06c30..5fde8e9c1e 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_unchangables.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_unchangables.json @@ -13,8 +13,24 @@ "roles": ["developer"] }, { - "type": "boolean", - "key": "is_host", - "label": "Has host implementation", + "type": "enum", + "key": "host_name", + "label": "Host implementation", + "enum_items": [ + {"": "< without host >"}, + {"aftereffects": "aftereffects"}, + {"blender": "blender"}, + {"celaction": "celaction"}, + {"fusion": "fusion"}, + {"harmony": "harmony"}, + {"hiero": "hiero"}, + {"houdini": "houdini"}, + {"maya": "maya"}, + {"nuke": "nuke"}, + {"photoshop": "photoshop"}, + {"resolve": "resolve"}, + {"tvpaint": "tvpaint"}, + {"unreal": "unreal"} + ], "roles": ["developer"] }] From 3393933e28071959b02f1702d0a53a652d71d9db Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:35:54 +0100 Subject: [PATCH 141/219] application manager load app_group and host_name --- pype/lib/applications.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index c1c6fc9301..37eb4f9e71 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -530,12 +530,12 @@ class ApplicationManager: settings = system_settings() hosts_definitions = settings["global"]["applications"] - for host_name, variant_definitions in hosts_definitions.items(): + for app_group, variant_definitions in hosts_definitions.items(): enabled = variant_definitions["enabled"] - label = variant_definitions.get("label") or host_name + label = variant_definitions.get("label") or app_group variants = variant_definitions.get("variants") or {} icon = variant_definitions.get("icon") - is_host = variant_definitions.get("is_host", False) + host_name = variant_definitions.get("host_name") or None for app_name, app_data in variants.items(): if app_name in self.applications: raise AssertionError(( @@ -553,11 +553,10 @@ class ApplicationManager: if not app_data.get("icon"): app_data["icon"] = icon - is_host = app_data.get("is_host", is_host) - app_data["is_host"] = is_host + app_data["is_host"] = host_name is not None self.applications[app_name] = Application( - host_name, app_name, app_data, self + app_group, app_name, host_name, app_data, self ) tools_definitions = settings["global"]["tools"] @@ -635,19 +634,21 @@ class Application: Object by itself does nothing special. Args: - host_name (str): Host name or rather name of host implementation. + app_group (str): App group name. e.g. "maya", "nuke", "photoshop", etc. app_name (str): Specific version (or variant) of host. e.g. "maya2020", "nuke11.3", etc. + host_name (str): Name of host implementation. app_data (dict): Data for the version containing information about executables, label, variant label, icon or if is enabled. Only required key is `executables`. manager (ApplicationManager): Application manager that created object. """ - def __init__(self, host_name, app_name, app_data, manager): - self.host_name = host_name + def __init__(self, app_group, app_name, host_name, app_data, manager): + self.app_group = app_group self.app_name = app_name + self.host_name = host_name self.app_data = app_data self.manager = manager @@ -768,6 +769,10 @@ class LaunchHook: def host_name(self): return getattr(self.application, "host_name", None) + @property + def app_group(self): + return getattr(self.application, "app_group", None) + @property def app_name(self): return getattr(self.application, "app_name", None) @@ -1010,6 +1015,10 @@ class ApplicationLaunchContext: def host_name(self): return self.application.host_name + @property + def app_group(self): + return self.application.app_group + @property def manager(self): return self.application.manager From c8c8424cb7ac4689fea14e140133e172f29907f8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:37:52 +0100 Subject: [PATCH 142/219] hooks filtering works for app_groups and app_names --- pype/lib/applications.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 37eb4f9e71..ac45112511 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -710,6 +710,10 @@ class LaunchHook: order = None # List of host implementations, skipped if empty. hosts = [] + # List of application groups + app_groups = [] + # List of specific application names + app_names = [] # List of platform availability, skipped if empty. platforms = [] @@ -751,6 +755,14 @@ class LaunchHook: if launch_context.host_name not in cls.hosts: return False + if cls.app_groups: + if launch_context.app_group not in cls.app_groups: + return False + + if cls.app_names: + if launch_context.app_name not in cls.app_names: + return False + return True @property From f8868d04bca8bd4951f0ee8958a668c11dbeaafd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:39:17 +0100 Subject: [PATCH 143/219] environments are per app group not per host name --- pype/hooks/global/pre_global_host_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/hooks/global/pre_global_host_data.py b/pype/hooks/global/pre_global_host_data.py index 787460019d..3f403b43f5 100644 --- a/pype/hooks/global/pre_global_host_data.py +++ b/pype/hooks/global/pre_global_host_data.py @@ -86,7 +86,7 @@ class GlobalHostDataHook(PreLaunchHook): def prepare_host_environments(self): """Modify launch environments based on launched app and context.""" # Keys for getting environments - env_keys = [self.host_name, self.app_name] + env_keys = [self.app_group, self.app_name] asset_doc = self.data.get("asset_doc") if asset_doc: From 19e5ab8b1a28120d6b31d43b4310cfa6495bebc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 10:40:21 +0100 Subject: [PATCH 144/219] changed current filtering from hosts to app groups --- pype/hooks/aftereffects/pre_launch_args.py | 2 +- pype/hooks/celaction/pre_celaction_registers.py | 2 +- pype/hooks/fusion/pre_fusion_setup.py | 2 +- pype/hooks/harmony/pre_launch_args.py | 2 +- pype/hooks/hiero/pre_launch_args.py | 2 +- pype/hooks/maya/pre_launch_args.py | 2 +- pype/hooks/nukestudio/pre_launch_args.py | 2 +- pype/hooks/nukex/pre_launch_args.py | 2 +- pype/hooks/photoshop/pre_launch_args.py | 2 +- pype/hooks/resolve/pre_resolve_setup.py | 2 +- pype/hooks/tvpaint/pre_install_pywin.py | 2 +- pype/hooks/tvpaint/pre_launch_args.py | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pype/hooks/aftereffects/pre_launch_args.py b/pype/hooks/aftereffects/pre_launch_args.py index 01406d495c..e39247b983 100644 --- a/pype/hooks/aftereffects/pre_launch_args.py +++ b/pype/hooks/aftereffects/pre_launch_args.py @@ -9,7 +9,7 @@ class AfterEffectsPrelaunchHook(PreLaunchHook): Hook add python executable and execute python script of AfterEffects implementation before AfterEffects executable. """ - hosts = ["aftereffects"] + app_groups = ["aftereffects"] def execute(self): # Pop tvpaint executable diff --git a/pype/hooks/celaction/pre_celaction_registers.py b/pype/hooks/celaction/pre_celaction_registers.py index f592026699..3f9d81fb98 100644 --- a/pype/hooks/celaction/pre_celaction_registers.py +++ b/pype/hooks/celaction/pre_celaction_registers.py @@ -13,7 +13,7 @@ class CelactionPrelaunchHook(PreLaunchHook): shell script. """ workfile_ext = "scn" - hosts = ["celaction"] + app_groups = ["celaction"] platforms = ["windows"] def execute(self): diff --git a/pype/hooks/fusion/pre_fusion_setup.py b/pype/hooks/fusion/pre_fusion_setup.py index ac7dda4250..d4402e9a04 100644 --- a/pype/hooks/fusion/pre_fusion_setup.py +++ b/pype/hooks/fusion/pre_fusion_setup.py @@ -9,7 +9,7 @@ class FusionPrelaunch(PreLaunchHook): This hook will check if current workfile path has Fusion project inside. """ - hosts = ["fusion"] + app_groups = ["fusion"] def execute(self): # making sure pyton 3.6 is installed at provided path diff --git a/pype/hooks/harmony/pre_launch_args.py b/pype/hooks/harmony/pre_launch_args.py index 70fac5bb76..70c05eb352 100644 --- a/pype/hooks/harmony/pre_launch_args.py +++ b/pype/hooks/harmony/pre_launch_args.py @@ -9,7 +9,7 @@ class HarmonyPrelaunchHook(PreLaunchHook): Hook add python executable and execute python script of harmony implementation before harmony executable. """ - hosts = ["harmony"] + app_groups = ["harmony"] def execute(self): # Pop tvpaint executable diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index 7139f57bbb..feca6dc3eb 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -4,7 +4,7 @@ from pype.lib import PreLaunchHook class HieroLaunchArguments(PreLaunchHook): order = 0 - hosts = ["hiero"] + app_groups = ["hiero"] def execute(self): """Prepare suprocess launch arguments for Hiero.""" diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py index 570c8e79b8..8b37bac15b 100644 --- a/pype/hooks/maya/pre_launch_args.py +++ b/pype/hooks/maya/pre_launch_args.py @@ -5,7 +5,7 @@ from pype.lib import PreLaunchHook class MayaLaunchArguments(PreLaunchHook): """Add path to last workfile to launch arguments.""" order = 0 - hosts = ["maya"] + app_groups = ["maya"] def execute(self): """Prepare suprocess launch arguments for Maya.""" diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index a5e04bf956..e572ca32a2 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -4,7 +4,7 @@ from pype.lib import PreLaunchHook class NukeStudioLaunchArguments(PreLaunchHook): order = 0 - hosts = ["nukestudio"] + app_groups = ["nukestudio"] def execute(self): """Prepare suprocess launch arguments for NukeStudio.""" diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index 39ccb5a58a..f0e5cf7733 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -4,7 +4,7 @@ from pype.lib import PreLaunchHook class NukeXLaunchArguments(PreLaunchHook): order = 0 - hosts = ["nukex"] + app_groups = ["nukex"] def execute(self): """Prepare suprocess launch arguments for NukeX.""" diff --git a/pype/hooks/photoshop/pre_launch_args.py b/pype/hooks/photoshop/pre_launch_args.py index 2c88f62157..b13e7d1e0f 100644 --- a/pype/hooks/photoshop/pre_launch_args.py +++ b/pype/hooks/photoshop/pre_launch_args.py @@ -9,7 +9,7 @@ class PhotoshopPrelaunchHook(PreLaunchHook): Hook add python executable and execute python script of photoshop implementation before photoshop executable. """ - hosts = ["photoshop"] + app_groups = ["photoshop"] def execute(self): # Pop tvpaint executable diff --git a/pype/hooks/resolve/pre_resolve_setup.py b/pype/hooks/resolve/pre_resolve_setup.py index 336fabf0c4..4f6d33c6eb 100644 --- a/pype/hooks/resolve/pre_resolve_setup.py +++ b/pype/hooks/resolve/pre_resolve_setup.py @@ -11,7 +11,7 @@ class ResolvePrelaunch(PreLaunchHook): path to the project by environment variable to Premiere launcher shell script. """ - hosts = ["resolve"] + app_groups = ["resolve"] def execute(self): # making sure pyton 3.6 is installed at provided path diff --git a/pype/hooks/tvpaint/pre_install_pywin.py b/pype/hooks/tvpaint/pre_install_pywin.py index b51267934b..ca9242c4c8 100644 --- a/pype/hooks/tvpaint/pre_install_pywin.py +++ b/pype/hooks/tvpaint/pre_install_pywin.py @@ -9,7 +9,7 @@ class PreInstallPyWin(PreLaunchHook): """Hook makes sure there is installed python module pywin32 on windows.""" # WARNING This hook will probably be deprecated in Pype 3 - kept for test order = 10 - hosts = ["tvpaint"] + app_groups = ["tvpaint"] platforms = ["windows"] def execute(self): diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 9d80e6fddb..13ec320fa0 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -16,7 +16,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): Existence of last workfile is checked. If workfile does not exists tries to copy templated workfile from predefined path. """ - hosts = ["tvpaint"] + app_groups = ["tvpaint"] def execute(self): # Pop tvpaint executable From 622208c015ed16a3074a3326ebe15c8a963b62ab Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 12:54:23 +0100 Subject: [PATCH 145/219] implemented `ApplicationExecutable` for handlind executablesx and arguments --- pype/lib/applications.py | 68 ++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index ac45112511..86c640f2ed 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -58,8 +58,8 @@ class ApplictionExecutableNotFound(Exception): " are not available on this machine." ) details = "Defined paths:" - for executable_path in application.executables: - details += "\n- " + executable_path + for executable in application.executables: + details += "\n- " + executable.executable_path self.msg = msg.format(application.full_label, application.app_name) self.details = details @@ -535,7 +535,7 @@ class ApplicationManager: label = variant_definitions.get("label") or app_group variants = variant_definitions.get("variants") or {} icon = variant_definitions.get("icon") - host_name = variant_definitions.get("host_name") or None + group_host_name = variant_definitions.get("host_name") or None for app_name, app_data in variants.items(): if app_name in self.applications: raise AssertionError(( @@ -553,6 +553,8 @@ class ApplicationManager: if not app_data.get("icon"): app_data["icon"] = icon + host_name = app_data.get("host_name") or group_host_name + app_data["is_host"] = host_name is not None self.applications[app_name] = Application( @@ -628,6 +630,41 @@ class ApplicationTool: return self.enabled +class ApplicationExecutable: + def __init__(self, executable): + default_launch_args = [] + if isinstance(executable, str): + executable_path = executable + + elif isinstance(executable, list): + executable_path = None + for arg in executable: + if arg: + if executable_path is None: + executable_path = arg + else: + default_launch_args.append(arg) + + self.executable_path = executable_path + self.default_launch_args = default_launch_args + + def __iter__(self): + yield self.executable_path + for arg in self.default_launch_args: + yield arg + + def __str__(self): + return self.executable_path + + def as_args(self): + return list(self) + + def exists(self): + if not self.executable_path: + return False + return os.path.exists(self.executable_path) + + class Application: """Hold information about application. @@ -658,12 +695,17 @@ class Application: self.enabled = app_data.get("enabled", True) self.is_host = app_data.get("is_host", False) - executables = app_data["executables"] - if isinstance(executables, dict): - executables = executables.get(platform.system().lower()) or [] + _executables = app_data["executables"] + if not _executables: + _executables = [] + + elif isinstance(_executables, dict): + _executables = _executables.get(platform.system().lower()) or [] + + executables = [] + for executable in _executables: + executables.append(ApplicationExecutable(executable)) - if not isinstance(executables, list): - executables = [executables] self.executables = executables @property @@ -683,9 +725,9 @@ class Application: Returns (str): Path to executable from `executables` or None if any exists. """ - for executable_path in self.executables: - if os.path.exists(executable_path): - return executable_path + for executable in self.executables: + if executable.exists(): + return executable return None def launch(self, *args, **kwargs): @@ -845,7 +887,7 @@ class ApplicationLaunchContext: Args: application (Application): Application definition. - executable (str): Path to executable. + executable (ApplicationExecutable): Object with path to executable. **data (dict): Any additional data. Data may be used during preparation to store objects usable in multiple places. """ @@ -868,7 +910,7 @@ class ApplicationLaunchContext: self.data["settings_env"] = settings_env # subprocess.Popen launch arguments (first argument in constructor) - self.launch_args = [executable] + self.launch_args = executable.as_args() # Handle launch environemtns passed_env = self.data.pop("env", None) From 7ab49cdf935229eaee59787e452c20bee3801bf3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 13:00:42 +0100 Subject: [PATCH 146/219] fix check of executable path as it does not have to be full path --- pype/lib/applications.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 86c640f2ed..fbf949c247 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -1,13 +1,12 @@ import os import sys -import re import getpass -import json import copy import platform import inspect import logging import subprocess +import distutils.spawn from abc import ABCMeta, abstractmethod import six @@ -649,7 +648,7 @@ class ApplicationExecutable: self.default_launch_args = default_launch_args def __iter__(self): - yield self.executable_path + yield distutils.spawn.find_executable(self.executable_path) for arg in self.default_launch_args: yield arg @@ -662,7 +661,7 @@ class ApplicationExecutable: def exists(self): if not self.executable_path: return False - return os.path.exists(self.executable_path) + return bool(distutils.spawn.find_executable(self.executable_path)) class Application: From c16e925cbe43d7ca103981e48a6e319357e042c0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 13:25:07 +0100 Subject: [PATCH 147/219] removed PathInput --- .../settings/settings/widgets/item_types.py | 3 +- .../settings/settings/widgets/widgets.py | 29 ------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index fd5364ea17..072d0a3ee6 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -4,7 +4,6 @@ from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ExpandingWidget, NumberSpinBox, - PathInput, GridLabelWidget, ComboBox, NiceCheckbox @@ -1094,7 +1093,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): layout.addWidget(label_widget, 0) self.label_widget = label_widget - self.input_field = PathInput(self) + self.input_field = QtWidgets.QLineEdit(self) self.setFocusProxy(self.input_field) layout.addWidget(self.input_field, 1) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index b64b1aa8ac..d25cd5a8e0 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -47,35 +47,6 @@ class ComboBox(QtWidgets.QComboBox): return self.itemData(self.currentIndex(), role=QtCore.Qt.UserRole) -class PathInput(QtWidgets.QLineEdit): - def clear_end_path(self): - value = self.text().strip() - if value.endswith("/"): - while value and value[-1] == "/": - value = value[:-1] - self.setText(value) - - def keyPressEvent(self, event): - # Always change backslash `\` for forwardslash `/` - if event.key() == QtCore.Qt.Key_Backslash: - event.accept() - new_event = QtGui.QKeyEvent( - event.type(), - QtCore.Qt.Key_Slash, - event.modifiers(), - "/", - event.isAutoRepeat(), - event.count() - ) - QtWidgets.QApplication.sendEvent(self, new_event) - return - super(PathInput, self).keyPressEvent(event) - - def focusOutEvent(self, event): - super(PathInput, self).focusOutEvent(event) - self.clear_end_path() - - class ClickableWidget(QtWidgets.QWidget): clicked = QtCore.Signal() From a763088d97433f5273c7d5d6d5dd653fcef73f72 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 13:27:00 +0100 Subject: [PATCH 148/219] path input and path widget may be with arguments --- .../settings/settings/widgets/item_types.py | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 072d0a3ee6..167afdf4ce 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1069,7 +1069,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): class PathInputWidget(QtWidgets.QWidget, InputObject): default_input_value = "" value_changed = QtCore.Signal(object) - valid_value_types = (str, ) + valid_value_types = (str, list) def __init__( self, input_data, parent, @@ -1081,6 +1081,8 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.initial_attributes(input_data, parent, as_widget) + self.with_arguments = input_data.get("with_arguments", False) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -1094,21 +1096,36 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.label_widget = label_widget self.input_field = QtWidgets.QLineEdit(self) + self.args_input_field = None + if self.with_arguments: + self.input_field.setPlaceholderText("Executable path") + self.args_input_field = QtWidgets.QLineEdit(self) + self.args_input_field.setPlaceholderText("Arguments") + self.setFocusProxy(self.input_field) layout.addWidget(self.input_field, 1) - self.input_field.textChanged.connect(self._on_value_change) + if self.args_input_field: + layout.addWidget(self.args_input_field, 1) + self.args_input_field.textChanged.connect(self._on_value_change) + def set_value(self, value): self.validate_value(value) - self.input_field.setText(value) - def focusOutEvent(self, event): - self.input_field.clear_end_path() - super(PathInput, self).focusOutEvent(event) + if not isinstance(value, list): + self.input_field.setText(value) + elif self.with_arguments: + self.input_field.setText(value[0]) + self.args_input_field.setText(value[1]) + else: + self.input_field.setText(value[0]) def item_value(self): - return self.input_field.text() + path_value = self.input_field.text() + if self.with_arguments: + return [path_value, self.args_input_field.text()] + return path_value class EnumeratorWidget(QtWidgets.QWidget, InputObject): @@ -3154,6 +3171,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): self.multiplatform = input_data.get("multiplatform", False) self.multipath = input_data.get("multipath", False) + self.with_arguments = input_data.get("with_arguments", False) self.input_field = None @@ -3195,7 +3213,10 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def create_gui(self): if not self.multiplatform and not self.multipath: - input_data = {"key": self.key} + input_data = { + "key": self.key, + "with_arguments": self.with_arguments + } path_input = PathInputWidget( input_data, self, as_widget=True, label_widget=self.label_widget @@ -3209,7 +3230,10 @@ class PathWidget(QtWidgets.QWidget, SettingObject): if not self.multiplatform: item_schema = { "key": self.key, - "object_type": "path-input" + "object_type": { + "type": "path-input", + "with_arguments": self.with_arguments + } } input_widget = ListWidget( item_schema, self, @@ -3234,9 +3258,13 @@ class PathWidget(QtWidgets.QWidget, SettingObject): } if self.multipath: child_item["type"] = "list" - child_item["object_type"] = "path-input" + child_item["object_type"] = { + "type": "path-input", + "with_arguments": self.with_arguments + } else: child_item["type"] = "path-input" + child_item["with_arguments"] = self.with_arguments item_schema["children"].append(child_item) From c089184789dd118414e42c1f577e01965d014675 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 13:30:51 +0100 Subject: [PATCH 149/219] hosts executables have set `with_arguments` to true --- .../system_settings/global/applications.json | 185 ++++++++++++++---- .../host_settings/template_host_variant.json | 3 +- 2 files changed, 150 insertions(+), 38 deletions(-) diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index 7dd0057110..cce4c4f25e 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -39,11 +39,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2020\\bin\\maya.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2020\\bin\\maya.exe", + "" + ] ], "darwin": [], "linux": [ - "/usr/autodesk/maya2020/bin/maya" + [ + "/usr/autodesk/maya2020/bin/maya", + "" + ] ] }, "environment": { @@ -62,11 +68,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2019\\bin\\maya.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2019\\bin\\maya.exe", + "" + ] ], "darwin": [], "linux": [ - "/usr/autodesk/maya2019/bin/maya" + [ + "/usr/autodesk/maya2019/bin/maya", + "" + ] ] }, "environment": { @@ -85,11 +97,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2017\\bin\\maya.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2017\\bin\\maya.exe", + "" + ] ], "darwin": [], "linux": [ - "/usr/autodesk/maya2018/bin/maya" + [ + "/usr/autodesk/maya2018/bin/maya", + "" + ] ] }, "environment": { @@ -143,7 +161,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2020\\bin\\mayabatch.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2020\\bin\\mayabatch.exe", + "" + ] ], "darwin": [], "linux": [] @@ -164,7 +185,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2019\\bin\\mayabatch.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2019\\bin\\mayabatch.exe", + "" + ] ], "darwin": [], "linux": [] @@ -185,7 +209,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Autodesk\\Maya2018\\bin\\mayabatch.exe" + [ + "C:\\Program Files\\Autodesk\\Maya2018\\bin\\mayabatch.exe", + "" + ] ], "darwin": [], "linux": [] @@ -230,11 +257,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe", + "" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" + [ + "/usr/local/Nuke12.0v1/Nuke12.0", + "" + ] ] }, "environment": { @@ -250,11 +283,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe", + "" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" + [ + "/usr/local/Nuke11.3v5/Nuke11.3", + "" + ] ] }, "environment": { @@ -270,7 +309,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" + [ + "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe", + "" + ] ], "darwin": [], "linux": [] @@ -312,11 +354,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe", + "--nukex" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" + [ + "/usr/local/Nuke12.0v1/Nuke12.0", + "--nukex" + ] ] }, "environment": { @@ -332,11 +380,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe", + "--nukex" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" + [ + "/usr/local/Nuke11.3v5/Nuke11.3", + "--nukex" + ] ] }, "environment": { @@ -352,7 +406,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" + [ + "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe", + "--nukex" + ] ], "darwin": [], "linux": [] @@ -398,11 +455,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe", + "--studio" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" + [ + "/usr/local/Nuke12.0v1/Nuke12.0", + "--studio" + ] ] }, "environment": { @@ -418,11 +481,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe", + "--studio" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" + [ + "/usr/local/Nuke11.3v5/Nuke11.3", + "--studio" + ] ] }, "environment": { @@ -482,11 +551,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe", + "--hiero" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke12.0v1/Nuke12.0" + [ + "/usr/local/Nuke12.0v1/Nuke12.0", + "--hiero" + ] ] }, "environment": { @@ -502,11 +577,17 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe", + "--hiero" + ] ], "darwin": [], "linux": [ - "/usr/local/Nuke11.3v5/Nuke11.3" + [ + "/usr/local/Nuke11.3v5/Nuke11.3", + "--hiero" + ] ] }, "environment": { @@ -522,7 +603,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" + [ + "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe", + "--hiero" + ] ], "darwin": [], "linux": [] @@ -843,7 +927,10 @@ "executables": { "windows": [], "darwin": [ - "/Applications/Toon Boom Harmony 17 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium" + [ + "/Applications/Toon Boom Harmony 17 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium", + "" + ] ], "linux": [] }, @@ -876,7 +963,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 (64bits)\\TVPaint Animation 11 (64bits).exe" + [ + "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 (64bits)\\TVPaint Animation 11 (64bits).exe", + "" + ] ], "darwin": [], "linux": [] @@ -894,7 +984,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files (x86)\\TVPaint Developpement\\TVPaint Animation 11 (32bits)\\TVPaint Animation 11 (32bits).exe" + [ + "C:\\Program Files (x86)\\TVPaint Developpement\\TVPaint Animation 11 (32bits)\\TVPaint Animation 11 (32bits).exe", + "" + ] ], "darwin": [], "linux": [] @@ -936,7 +1029,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe" + [ + "C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe", + "" + ] ], "darwin": [], "linux": [] @@ -954,7 +1050,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe" + [ + "C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe", + "" + ] ], "darwin": [], "linux": [] @@ -996,7 +1095,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2020\\Support Files\\AfterFX.exe" + [ + "C:\\Program Files\\Adobe\\Adobe After Effects 2020\\Support Files\\AfterFX.exe", + "" + ] ], "darwin": [], "linux": [] @@ -1014,7 +1116,10 @@ "icon": "", "executables": { "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2021\\Support Files\\AfterFX.exe" + [ + "C:\\Program Files\\Adobe\\Adobe After Effects 2021\\Support Files\\AfterFX.exe", + "" + ] ], "darwin": [], "linux": [] @@ -1046,7 +1151,10 @@ "label": "", "variant_label": "Local", "icon": "{}/app_icons/celaction_local.png", - "executables": "", + "executables": [ + "", + "" + ], "environment": { "__environment_keys__": { "celation_Local": [] @@ -1058,7 +1166,10 @@ "label": "", "variant_label": "Pulblish", "icon": "", - "executables": "", + "executables": [ + "", + "" + ], "environment": { "__environment_keys__": { "celation_Publish": [] diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_variant.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_variant.json index ce3a75e871..cea7da3a81 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_variant.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_host_variant.json @@ -43,7 +43,8 @@ "key": "executables", "label": "Executables", "multiplatform": "{multiplatform}", - "multipath": "{multipath_executables}" + "multipath": "{multipath_executables}", + "with_arguments": true }, { "key": "environment", From 4b3374ac8b14e55503f47fdd5580ba1ff665846c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 13:38:51 +0100 Subject: [PATCH 150/219] avoid accidental combobox value changes on wheel event --- pype/tools/settings/settings/widgets/widgets.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index d25cd5a8e0..dc2ccabe10 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -32,6 +32,11 @@ class ComboBox(QtWidgets.QComboBox): super(ComboBox, self).__init__(*args, **kwargs) self.currentIndexChanged.connect(self._on_change) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if self.hasFocus(): + return super(ComboBox, self).wheelEvent(event) def _on_change(self, *args, **kwargs): self.value_changed.emit() From e99fc55bd4c233adbb6bedc71f0a124a55bb516c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 17:39:56 +0100 Subject: [PATCH 151/219] renamed labeled_items to collapsable_key --- .../settings/settings/widgets/item_types.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d4942e04d2..25bbe4a515 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1975,7 +1975,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.origin_key = NOT_SET self.origin_key_label = NOT_SET - if self.labeled_items: + if self.collapsable_key: layout = QtWidgets.QVBoxLayout(self) else: layout = QtWidgets.QHBoxLayout(self) @@ -1996,7 +1996,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_label_input = None wrapper_widget = None - if self.labeled_items: + if self.collapsable_key: key_label_input = QtWidgets.QLineEdit(self) wrapper_widget = ExpandingWidget("", self) @@ -2024,7 +2024,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_label_input.focusOutEvent = key_label_input_focused_out spacer_widget = None - if not self.labeled_items: + if not self.collapsable_key: spacer_widget = QtWidgets.QWidget(self) spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) spacer_widget.setVisible(False) @@ -2035,7 +2035,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): add_btn.setFixedSize(self._btn_size, self._btn_size) edit_btn = None - if self.labeled_items: + if self.collapsable_key: edit_btn = IconButton( "fa.edit", QtCore.Qt.lightGray, QtCore.Qt.white ) @@ -2050,7 +2050,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_input_label_widget = None key_label_input_label_widget = None - if self.labeled_items: + if self.collapsable_key: key_input_label_widget = QtWidgets.QLabel("Key:") key_label_input_label_widget = QtWidgets.QLabel("Label:") wrapper_widget.add_widget_before_label(add_btn) @@ -2116,8 +2116,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return state @property - def labeled_items(self): - return self._parent.labeled_items + def collapsable_key(self): + return self._parent.collapsable_key def key_value(self): return self.key_input.text() @@ -2138,7 +2138,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._is_key_duplicated = duplicated - if self.labeled_items: + if self.collapsable_key: if duplicated: self.set_edit_mode(True) else: @@ -2156,7 +2156,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._on_enter_press() def _on_enter_press(self): - if not self.labeled_items: + if not self.collapsable_key: return if self._is_empty: @@ -2226,7 +2226,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.wrapper_widget.label_widget.setText(label) def on_add_clicked(self): - if not self.labeled_items: + if not self.collapsable_key: if self._is_empty: self.set_as_empty(False) else: @@ -2268,7 +2268,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.value_input.setVisible(not is_empty) self.add_btn.setVisible(is_empty) - if not self.labeled_items: + if not self.collapsable_key: self.key_input.setVisible(not is_empty) self.remove_btn.setEnabled(not is_empty) self.spacer_widget.setVisible(is_empty) @@ -2362,7 +2362,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._parent.input_fields.index(self) def key_label_value(self): - if self.labeled_items: + if self.collapsable_key: return self.key_label_input.text() return NOT_SET @@ -2406,7 +2406,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): schema_data.get("value_is_env_group") or False ) self.hightlight_content = schema_data.get("highlight_content") or False - self.labeled_items = schema_data.get("labeled_items") or False + self.collapsable_key = schema_data.get("collapsable_key") or False object_type = schema_data["object_type"] if isinstance(object_type, dict): @@ -2505,7 +2505,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): label = dynamic_key_labels.get(item_key) self.add_row(key=item_key, label=label, value=item_value) - if self.labeled_items: + if self.collapsable_key: self.add_row(is_empty=True) for input_field in previous_inputs: @@ -2597,7 +2597,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): return output def item_value_with_metadata(self): - if not self.labeled_items: + if not self.collapsable_key: output = self.item_value() else: @@ -2653,7 +2653,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.input_fields.insert(row, item_widget) previous_input = None - if self.labeled_items: + if self.collapsable_key: for input_field in self.input_fields: if previous_input is not None: self.setTabOrder( From 103e17f7b309b8012ad8bcb285e5418d32ed795d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Nov 2020 17:51:18 +0100 Subject: [PATCH 152/219] fix settings loading --- pype/lib/applications.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index fbf949c247..ddff9ddcd2 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -528,7 +528,7 @@ class ApplicationManager: """Refresh applications from settings.""" settings = system_settings() - hosts_definitions = settings["global"]["applications"] + hosts_definitions = settings["applications"] for app_group, variant_definitions in hosts_definitions.items(): enabled = variant_definitions["enabled"] label = variant_definitions.get("label") or app_group @@ -560,7 +560,7 @@ class ApplicationManager: app_group, app_name, host_name, app_data, self ) - tools_definitions = settings["global"]["tools"] + tools_definitions = settings["tools"] for tool_group_name, tool_group_data in tools_definitions.items(): enabled = tool_group_data.get("enabled", True) tool_variants = tool_group_data.get("variants") or {} From 8b1a74547a893a8b8a4388a6cf35b5808a07acc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:01:55 +0100 Subject: [PATCH 153/219] frames:v argument is used only if output is sequence --- pype/plugins/global/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index d08748209d..75d7682dd4 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -387,7 +387,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args.append("-t {:0.2f}".format(duration_sec)) # Set frame range of output when input or output is sequence - elif temp_data["input_is_sequence"] or temp_data["output_is_sequence"]: + elif temp_data["output_is_sequence"]: ffmpeg_output_args.append("-frames:v {}".format(output_frames_len)) # Add video/image input path From 86a2256a8f0dfd4ef14afdfeabe639453355fe6c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:03:57 +0100 Subject: [PATCH 154/219] add duration of input in seconds is is sequence and output is video --- pype/plugins/global/publish/extract_review.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 75d7682dd4..2340253066 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -348,6 +348,8 @@ class ExtractReview(pyblish.api.InstancePlugin): + 1 ) + duration_seconds = float(output_frames_len / temp_data["fps"]) + if temp_data["input_is_sequence"]: # Set start frame of input sequence (just frame in filename) # - definition of input filepath @@ -375,21 +377,30 @@ class ExtractReview(pyblish.api.InstancePlugin): # Change output's duration and start point if should not contain # handles + start_sec = 0 if temp_data["without_handles"] and temp_data["handles_are_set"]: # Set start time without handles # - check if handle_start is bigger than 0 to avoid zero division if temp_data["handle_start"] > 0: start_sec = float(temp_data["handle_start"]) / temp_data["fps"] - ffmpeg_input_args.append("-ss {:0.2f}".format(start_sec)) + ffmpeg_input_args.append("-ss {:0.10f}".format(start_sec)) # Set output duration inn seconds - duration_sec = float(output_frames_len / temp_data["fps"]) - ffmpeg_output_args.append("-t {:0.2f}".format(duration_sec)) + ffmpeg_output_args.append("-t {:0.10}".format(duration_seconds)) # Set frame range of output when input or output is sequence elif temp_data["output_is_sequence"]: ffmpeg_output_args.append("-frames:v {}".format(output_frames_len)) + # Add duration of an input sequence if output is video + if ( + temp_data["input_is_sequence"] + and not temp_data["output_is_sequence"] + ): + ffmpeg_input_args.append("-to {:0.10f}".format( + duration_seconds + start_sec + )) + # Add video/image input path ffmpeg_input_args.append( "-i \"{}\"".format(temp_data["full_input_path"]) From e7ed093bfaefe641ed2b76e55e23a70e8626c940 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:04:17 +0100 Subject: [PATCH 155/219] set audio duration to full length of output --- pype/plugins/global/publish/extract_review.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 2340253066..9bdc2103b1 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -412,7 +412,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # Add audio arguments if there are any. Skipped when output are images. if not temp_data["output_ext_is_image"] and temp_data["with_audio"]: audio_in_args, audio_filters, audio_out_args = self.audio_args( - instance, temp_data + instance, temp_data, duration_seconds ) ffmpeg_input_args.extend(audio_in_args) ffmpeg_audio_filters.extend(audio_filters) @@ -627,7 +627,7 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("Input path {}".format(full_input_path)) self.log.debug("Output path {}".format(full_output_path)) - def audio_args(self, instance, temp_data): + def audio_args(self, instance, temp_data, duration_seconds): """Prepares FFMpeg arguments for audio inputs.""" audio_in_args = [] audio_filters = [] @@ -650,11 +650,19 @@ class ExtractReview(pyblish.api.InstancePlugin): audio_in_args.append( "-ss {}".format(offset_seconds) ) + elif offset_seconds < 0: audio_in_args.append( "-itsoffset {}".format(abs(offset_seconds)) ) + # Audio duration is offset from `-ss` + audio_duration = duration_seconds + offset_seconds + + # Set audio duration + audio_in_args.append("-to {:0.10f}".format(audio_duration)) + + # Add audio input path audio_in_args.append("-i \"{}\"".format(audio["filename"])) # NOTE: These were changed from input to output arguments. From ef2b8ac1320cd42c8529d3e6714c703f3c53b62c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:04:26 +0100 Subject: [PATCH 156/219] shortest is not needed anymore --- pype/plugins/global/publish/extract_review.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 9bdc2103b1..aa8d8accb5 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -406,9 +406,6 @@ class ExtractReview(pyblish.api.InstancePlugin): "-i \"{}\"".format(temp_data["full_input_path"]) ) - # Use shortest input - ffmpeg_output_args.append("-shortest") - # Add audio arguments if there are any. Skipped when output are images. if not temp_data["output_ext_is_image"] and temp_data["with_audio"]: audio_in_args, audio_filters, audio_out_args = self.audio_args( From 0073903eceef8dd6617c5c24172e8db5cbfd3d0a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 1 Dec 2020 16:38:53 +0100 Subject: [PATCH 157/219] change executable and arguments visual ratio --- pype/tools/settings/settings/widgets/item_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1fe8003716..8a1788188d 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1103,11 +1103,11 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.args_input_field.setPlaceholderText("Arguments") self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 1) + layout.addWidget(self.input_field, 8) self.input_field.textChanged.connect(self._on_value_change) if self.args_input_field: - layout.addWidget(self.args_input_field, 1) + layout.addWidget(self.args_input_field, 2) self.args_input_field.textChanged.connect(self._on_value_change) def set_value(self, value): From 529bca3f9c08779573066ca0dff2790b7cfc16b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 17:09:41 +0100 Subject: [PATCH 158/219] fixed defaults save in projects --- pype/tools/settings/settings/widgets/base.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index d3128d7546..2e5c2f356b 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -239,14 +239,6 @@ class SystemWidget(QtWidgets.QWidget): if not self.items_are_valid(): return - output = {} - for item in self.input_fields: - output.update(item.config_value()) - - for key in reversed(self.keys): - _output = {key: output} - output = _output - all_values = {} for item in self.input_fields: all_values.update(item.config_value()) @@ -626,6 +618,8 @@ class ProjectWidget(QtWidgets.QWidget): for item in self.input_fields: all_values.update(item.config_value()) + all_values = lib.convert_gui_data_with_metadata(all_values) + for key in reversed(self.keys): _all_values = {key: all_values} all_values = _all_values From 8fb95d5c52054740cc0b050ebd8673a1ff1fd8e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:52:40 +0100 Subject: [PATCH 159/219] collector collect context from workfile metadata and update it (except project name) --- .../tvpaint/publish/collect_workfile_data.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index 31fd97ced4..e26e45a1e4 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -1,6 +1,8 @@ +import os import json import pyblish.api +import avalon.api from avalon.tvpaint import pipeline, lib @@ -10,26 +12,58 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): hosts = ["tvpaint"] def process(self, context): + # Collect and store current context to have reference + current_context = { + "project": avalon.api.Session["AVALON_PROJECT"], + "asset": avalon.api.Session["AVALON_ASSET"], + "task": avalon.api.Session["AVALON_TASK"] + } + context.data["previous_context"] = current_context + self.log.debug("Current context is: {}".format(current_context)) + + # Collect context from workfile metadata + self.log.info("Collecting workfile context") + workfile_context = pipeline.get_current_workfile_context() + if workfile_context: + # Change current context with context from workfile + key_map = (("AVALON_ASSET", "asset"), ("AVALON_TASK", "task")) + for env_key, key in key_map: + avalon.api.Session[env_key] = workfile_context[key] + os.environ[env_key] = workfile_context[key] + else: + # Handle older workfiles or workfiles without metadata + self.log.warning( + "Workfile does not contain information about context." + " Using current Session context." + ) + workfile_context = current_context.copy() + + context.data["workfile_context"] = workfile_context + self.log.info("Context changed to: {}".format(workfile_context)) + + # Collect instances self.log.info("Collecting instance data from workfile") instance_data = pipeline.list_instances() + context.data["workfileInstances"] = instance_data self.log.debug( "Instance data:\"{}".format(json.dumps(instance_data, indent=4)) ) - context.data["workfileInstances"] = instance_data + # Collect information about layers self.log.info("Collecting layers data from workfile") layers_data = lib.layers_data() + context.data["layersData"] = layers_data self.log.debug( "Layers data:\"{}".format(json.dumps(layers_data, indent=4)) ) - context.data["layersData"] = layers_data + # Collect information about groups self.log.info("Collecting groups data from workfile") group_data = lib.groups_data() + context.data["groupsData"] = group_data self.log.debug( "Group data:\"{}".format(json.dumps(group_data, indent=4)) ) - context.data["groupsData"] = group_data self.log.info("Collecting scene data from workfile") workfile_info_parts = lib.execute_george("tv_projectinfo").split(" ") From 1591cab85f2fc5d832ca3e2145f99b8dbbbdbcda Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:52:52 +0100 Subject: [PATCH 160/219] added validator for project name check --- .../publish/validate_workfile_project_name.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pype/plugins/tvpaint/publish/validate_workfile_project_name.py diff --git a/pype/plugins/tvpaint/publish/validate_workfile_project_name.py b/pype/plugins/tvpaint/publish/validate_workfile_project_name.py new file mode 100644 index 0000000000..7c1032fcad --- /dev/null +++ b/pype/plugins/tvpaint/publish/validate_workfile_project_name.py @@ -0,0 +1,37 @@ +import os +import pyblish.api + + +class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): + """Validate project name stored in workfile metadata. + + It is not possible to publish from different project than is set in + environment variable "AVALON_PROJECT". + """ + + label = "Validate Workfile Project Name" + order = pyblish.api.ValidatorOrder + + def process(self, context): + workfile_context = context.data["workfile_context"] + workfile_project_name = workfile_context["project"] + env_project_name = os.environ["AVALON_PROJECT"] + if workfile_project_name == env_project_name: + self.log.info(( + "Both workfile project and environment project are same. {}" + ).format(env_project_name)) + return + + # Raise an error + raise AssertionError(( + # Short message + "Workfile from different Project ({})." + # Description what's wrong + " It is not possible to publish when TVPaint was launched in" + "context of different project. Current context project is \"{}\"." + " Launch TVPaint in context of project \"{}\" and then publish." + ).format( + workfile_project_name, + env_project_name, + workfile_project_name, + )) From a0ffcfccaae5d417fe943af493991db60183a857 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:59:33 +0100 Subject: [PATCH 161/219] formatting change --- pype/plugins/tvpaint/publish/collect_workfile_data.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index e26e45a1e4..cf6113740c 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -26,7 +26,10 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): workfile_context = pipeline.get_current_workfile_context() if workfile_context: # Change current context with context from workfile - key_map = (("AVALON_ASSET", "asset"), ("AVALON_TASK", "task")) + key_map = ( + ("AVALON_ASSET", "asset"), + ("AVALON_TASK", "task") + ) for env_key, key in key_map: avalon.api.Session[env_key] = workfile_context[key] os.environ[env_key] = workfile_context[key] From 095883c0b0e3af917da931000ee66cd0d246b77b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 16:55:57 +0100 Subject: [PATCH 162/219] make sure current project is selected project --- pype/plugins/tvpaint/publish/collect_workfile_data.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index cf6113740c..c6179b76cf 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -12,6 +12,9 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): hosts = ["tvpaint"] def process(self, context): + current_project_id = lib.execute_george("tv_projectcurrentid") + lib.execute_george("tv_projectselect {}".format(current_project_id)) + # Collect and store current context to have reference current_context = { "project": avalon.api.Session["AVALON_PROJECT"], From d2a110c1a98579af79f31334a5ddb4eb7881b2b5 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 1 Dec 2020 18:06:24 +0100 Subject: [PATCH 163/219] update standalon publisher with collapsible keys --- .../defaults/project_anatomy/imageio.json | 239 +++++++++--------- .../project_settings/standalonepublisher.json | 14 +- .../schema_project_standalonepublisher.json | 1 + 3 files changed, 137 insertions(+), 117 deletions(-) diff --git a/pype/settings/defaults/project_anatomy/imageio.json b/pype/settings/defaults/project_anatomy/imageio.json index 98ded33370..4e98463ee4 100644 --- a/pype/settings/defaults/project_anatomy/imageio.json +++ b/pype/settings/defaults/project_anatomy/imageio.json @@ -1,122 +1,129 @@ { - "hiero": { - "workfile": { - "ocioConfigName": "nuke-default", - "ocioconfigpath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB" - }, - "regexInputs": { - "inputs": [{ - "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", - "colorspace": "sRGB" - }] - } - }, - "nuke": { - "workfile": { - "colorManagement": "Nuke", - "OCIO_config": "nuke-default", - "customOCIOConfigPath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpaceLUT": "linear", - "monitorLut": "sRGB", - "int8Lut": "sRGB", - "int16Lut": "sRGB", - "logLut": "Cineon", - "floatLut": "linear" - }, - "nodes": { - "requiredNodes": [{ - "plugins": [ - "CreateWriteRender" - ], - "nukeNodeClass": "Write", - "knobs": [{ - "name": "file_type", - "value": "exr" + "hiero": { + "workfile": { + "ocioConfigName": "nuke-default", + "ocioconfigpath": { + "windows": [], + "darwin": [], + "linux": [] }, - { - "name": "datatype", - "value": "16 bit half" - }, - { - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "name": "autocrop", - "value": "True" - }, - { - "name": "tile_color", - "value": "0xff0000ff" - }, - { - "name": "channels", - "value": "rgb" - }, - { - "name": "colorspace", - "value": "linear" - } - ] + "workingSpace": "linear", + "sixteenBitLut": "sRGB", + "eightBitLut": "sRGB", + "floatLut": "linear", + "logLut": "Cineon", + "viewerLut": "sRGB", + "thumbnailLut": "sRGB" }, - { - "plugins": [ - "CreateWritePrerender" - ], - "nukeNodeClass": "Write", - "knobs": [{ - "name": "file_type", - "value": "exr" - }, - { - "name": "datatype", - "value": "16 bit half" - }, - { - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "name": "autocrop", - "value": "False" - }, - { - "name": "tile_color", - "value": "0xff0000ff" - }, - { - "name": "channels", - "value": "rgb" - }, - { - "name": "colorspace", - "value": "linear" - } - ] + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", + "colorspace": "sRGB" + } + ] } - ], - "customNodes": [] }, - "regexInputs": { - "inputs": [{ - "regex": "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]", - "colorspace": "linear" - }] + "nuke": { + "workfile": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "customOCIOConfigPath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpaceLUT": "linear", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "nodes": { + "requiredNodes": [ + { + "plugins": [ + "CreateWriteRender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "True" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + }, + { + "plugins": [ + "CreateWritePrerender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "name": "file_type", + "value": "exr" + }, + { + "name": "datatype", + "value": "16 bit half" + }, + { + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "name": "autocrop", + "value": "False" + }, + { + "name": "tile_color", + "value": "0xff0000ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "linear" + } + ] + } + ], + "customNodes": [] + }, + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]", + "colorspace": "linear" + } + ] + } } - } -} +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json index 2b5db54a4f..877055ceef 100644 --- a/pype/settings/defaults/project_settings/standalonepublisher.json +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -10,6 +10,18 @@ } }, "create": { + "__dynamic_keys_labels__": { + "create_workfile": "Workfile", + "create_model": "Model", + "create_rig": "Rig", + "create_pointcache": "Pointcache", + "create_plate": "Plate", + "create_camera": "Camera", + "create_editorial": "Editorial", + "create_image": "Image", + "create_matchmove": "matchmove", + "": "" + }, "create_workfile": { "name": "workfile", "label": "Workfile", @@ -21,7 +33,7 @@ "help": "Working scene backup" }, "create_model": { - "name": "mode", + "name": "model", "label": "Model", "family": "model", "icon": "cube", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json index 4a6e5f76ec..1241620dea 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_standalonepublisher.json @@ -45,6 +45,7 @@ "collapsable": true, "key": "create", "label": "Creator plugins", + "collapsable_key": true, "is_file": true, "object_type": { From 2afe4484a63d6299ab38a595e758be1d45a92249 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 18:15:18 +0100 Subject: [PATCH 164/219] fixed conflict changes --- pype/tools/settings/settings/widgets/item_types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 7f78d672dd..1f5615a240 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1094,6 +1094,8 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.initial_attributes(schema_data, parent, as_widget) + self.with_arguments = schema_data.get("with_arguments", False) + def create_ui(self, label_widget=None): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -3205,6 +3207,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): self.multiplatform = schema_data.get("multiplatform", False) self.multipath = schema_data.get("multipath", False) + self.with_arguments = schema_data.get("with_arguments", False) self.input_field = None @@ -3244,8 +3247,11 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def create_ui_inputs(self): if not self.multiplatform and not self.multipath: - input_data = {"key": self.key} - path_input = PathInputWidget(input_data, self, as_widget=True) + item_schema = { + "key": self.key, + "with_arguments": self.with_arguments + } + path_input = PathInputWidget(item_schema, self, as_widget=True) path_input.create_ui(label_widget=self.label_widget) self.setFocusProxy(path_input) From f82e72fad370e827bc6eb89e275c12c0e3ad3d63 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 18:40:25 +0100 Subject: [PATCH 165/219] implemented base of wrapper items --- .../settings/settings/widgets/item_types.py | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 25bbe4a515..a0d98fd1c5 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3505,6 +3505,216 @@ class PathWidget(QtWidgets.QWidget, SettingObject): return value, self.is_group +class WrapperItemWidget(QtWidgets.QWidget, SettingObject): + value_changed = QtCore.Signal(object) + allow_actions = False + expand_in_grid = True + is_wrapper_item = True + + def __init__( + self, schema_data, parent, as_widget=False, parent_widget=None + ): + if parent_widget is None: + parent_widget = parent + super(WrapperItemWidget, self).__init__(parent_widget) + + self.input_fields = [] + + self.initial_attributes(schema_data, parent, as_widget) + + if self.as_widget: + raise TypeError( + "Wrapper items ({}) can't be used as widgets.".format( + self.__class__.__name__ + ) + ) + + if self.is_group: + raise TypeError( + "Wrapper items ({}) can't be used as groups.".format( + self.__class__.__name__ + ) + ) + + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + self.wrapper_initial_attributes(schema_data) + + def wrapper_initial_attributes(self, schema_data): + """Initialization of attributes for specific wrapper.""" + return + + def create_ui(self, label_widget=None): + """UI implementation.""" + raise NotImplementedError( + "Method `create_ui` not implemented." + ) + + def update_style(self): + """Update items styles.""" + return + + def apply_overrides(self, parent_values): + for item in self.input_fields: + item.apply_overrides(parent_values) + + def discard_changes(self): + self._is_modified = False + self._is_overriden = self._was_overriden + self._has_studio_override = self._had_studio_override + + for input_field in self.input_fields: + input_field.discard_changes() + + self._is_modified = self.child_modified + if not self.is_overidable and self.as_widget: + if self.has_studio_override: + self._is_modified = self.studio_value != self.item_value() + else: + self._is_modified = self.default_value != self.item_value() + + self._state = None + self._is_overriden = self._was_overriden + + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + for input_field in self.input_fields: + input_field.remove_overrides() + + def reset_to_pype_default(self): + for input_field in self.input_fields: + input_field.reset_to_pype_default() + self._has_studio_override = False + + def set_studio_default(self): + for input_field in self.input_fields: + input_field.set_studio_default() + + if self.is_group: + self._has_studio_override = True + + def set_as_overriden(self): + if self.is_overriden: + return + + if self.is_group: + self._is_overriden = True + return + + for item in self.input_fields: + item.set_as_overriden() + + def update_default_values(self, value): + for item in self.input_fields: + item.update_default_values(value) + + def update_studio_values(self, value): + for item in self.input_fields: + item.update_studio_values(value) + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + self.value_changed.emit(self) + if self.any_parent_is_group: + self.hierarchical_style_update() + + @property + def child_has_studio_override(self): + for input_field in self.input_fields: + if ( + input_field.has_studio_override + or input_field.child_has_studio_override + ): + return True + return False + + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.is_overriden or input_field.child_overriden: + return True + return False + + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False + + def get_invalid(self): + output = [] + for input_field in self.input_fields: + output.extend(input_field.get_invalid()) + return output + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return self.item_value() + + def studio_overrides(self): + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + and not self.child_has_studio_override + ): + return NOT_SET, False + + values = {} + groups = [] + for input_field in self.input_fields: + value, is_group = input_field.studio_overrides() + if value is not NOT_SET: + values.update(value) + if is_group: + groups.extend(value.keys()) + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return values, self.is_group + + def overrides(self): + if not self.is_overriden and not self.child_overriden: + return NOT_SET, False + + values = {} + groups = [] + for input_field in self.input_fields: + value, is_group = input_field.overrides() + if value is not NOT_SET: + values.update(value) + if is_group: + groups.extend(value.keys()) + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return values, self.is_group + + # Proxy for form layout class FormLabel(QtWidgets.QLabel): def __init__(self, *args, **kwargs): From a417d39631424b90a76dc8841a45396e475fe9ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 18:41:39 +0100 Subject: [PATCH 166/219] fixed label click --- .../settings/settings/widgets/item_types.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index a0d98fd1c5..1a04ac1a8a 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3717,9 +3717,14 @@ class WrapperItemWidget(QtWidgets.QWidget, SettingObject): # Proxy for form layout class FormLabel(QtWidgets.QLabel): - def __init__(self, *args, **kwargs): + def __init__(self, input_field, *args, **kwargs): super(FormLabel, self).__init__(*args, **kwargs) - self.item = None + self.input_field = input_field + + def mouseReleaseEvent(self, event): + if self.input_field: + return self.input_field.show_actions_menu(event) + return super(FormLabel, self).mouseReleaseEvent(event) class DictFormWidget(QtWidgets.QWidget, SettingObject): @@ -3767,9 +3772,10 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): klass = TypeToKlass.types.get(item_type) - label_widget = FormLabel(label, self) - item = klass(child_configuration, self) + + label_widget = FormLabel(item, label, self) + item.create_ui(label_widget=label_widget) label_widget.item = item @@ -3781,16 +3787,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.input_fields.append(item) return item - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.RightButton: - position = self.mapFromGlobal(QtGui.QCursor().pos()) - widget = self.childAt(position) - if widget and isinstance(widget, FormLabel): - widget.item.mouseReleaseEvent(event) - event.accept() - return - super(DictFormWidget, self).mouseReleaseEvent(event) - def apply_overrides(self, parent_values): for item in self.input_fields: item.apply_overrides(parent_values) From 5059064228c99aad375ecec00ce03439ac67f4a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 18:47:09 +0100 Subject: [PATCH 167/219] DictFormWidget inherit from and renamed to FormItemWidget --- .../settings/settings/widgets/item_types.py | 188 +----------------- 1 file changed, 4 insertions(+), 184 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1a04ac1a8a..629ae415ac 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3727,35 +3727,14 @@ class FormLabel(QtWidgets.QLabel): return super(FormLabel, self).mouseReleaseEvent(event) -class DictFormWidget(QtWidgets.QWidget, SettingObject): - value_changed = QtCore.Signal(object) - allow_actions = False - expand_in_grid = True - is_wrapper_item = True - - def __init__( - self, schema_data, parent, as_widget=False, parent_widget=None - ): - if parent_widget is None: - parent_widget = parent - super(DictFormWidget, self).__init__(parent_widget) - - self.initial_attributes(schema_data, parent, as_widget) - - self._as_widget = False - self._is_group = False - - self.input_fields = [] - +class FormItemWidget(WrapperItemWidget): def create_ui(self, label_widget=None): self.content_layout = QtWidgets.QFormLayout(self) self.content_layout.setContentsMargins(0, 0, 0, 0) - for child_data in self.schema_data.get("children", []): + for child_data in self.schema_data["children"]: self.add_children_gui(child_data) - self.setAttribute(QtCore.Qt.WA_TranslucentBackground) - any_visible = False for input_field in self.input_fields: if not input_field.hidden_by_role: @@ -3763,6 +3742,7 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): break if not any_visible: + self.hidden_by_role = True self.hide() def add_children_gui(self, child_configuration): @@ -3777,7 +3757,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): label_widget = FormLabel(item, label, self) item.create_ui(label_widget=label_widget) - label_widget.item = item if item.hidden_by_role: label_widget.hide() @@ -3787,165 +3766,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.input_fields.append(item) return item - def apply_overrides(self, parent_values): - for item in self.input_fields: - item.apply_overrides(parent_values) - - def discard_changes(self): - self._is_modified = False - self._is_overriden = self._was_overriden - self._has_studio_override = self._had_studio_override - - for input_field in self.input_fields: - input_field.discard_changes() - - self._is_modified = self.child_modified - if not self.is_overidable and self.as_widget: - if self.has_studio_override: - self._is_modified = self.studio_value != self.item_value() - else: - self._is_modified = self.default_value != self.item_value() - - self._state = None - self._is_overriden = self._was_overriden - - def remove_overrides(self): - self._is_overriden = False - self._is_modified = False - for input_field in self.input_fields: - input_field.remove_overrides() - - def reset_to_pype_default(self): - for input_field in self.input_fields: - input_field.reset_to_pype_default() - self._has_studio_override = False - - def set_studio_default(self): - for input_field in self.input_fields: - input_field.set_studio_default() - - if self.is_group: - self._has_studio_override = True - - def set_as_overriden(self): - if self.is_overriden: - return - - if self.is_group: - self._is_overriden = True - return - - for item in self.input_fields: - item.set_as_overriden() - - def update_default_values(self, value): - for item in self.input_fields: - item.update_default_values(value) - - def update_studio_values(self, value): - for item in self.input_fields: - item.update_studio_values(value) - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - self.value_changed.emit(self) - if self.any_parent_is_group: - self.hierarchical_style_update() - - @property - def child_has_studio_override(self): - for input_field in self.input_fields: - if ( - input_field.has_studio_override - or input_field.child_has_studio_override - ): - return True - return False - - @property - def child_modified(self): - for input_field in self.input_fields: - if input_field.child_modified: - return True - return False - - @property - def child_overriden(self): - for input_field in self.input_fields: - if input_field.is_overriden or input_field.child_overriden: - return True - return False - - @property - def child_invalid(self): - for input_field in self.input_fields: - if input_field.child_invalid: - return True - return False - - def get_invalid(self): - output = [] - for input_field in self.input_fields: - output.extend(input_field.get_invalid()) - return output - - def hierarchical_style_update(self): - for input_field in self.input_fields: - input_field.hierarchical_style_update() - - def item_value(self): - output = {} - for input_field in self.input_fields: - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output - - def config_value(self): - return self.item_value() - - def studio_overrides(self): - if ( - not (self.as_widget or self.any_parent_as_widget) - and not self.has_studio_override - and not self.child_has_studio_override - ): - return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - if METADATA_KEY not in values: - values[METADATA_KEY] = {} - values[METADATA_KEY]["groups"] = groups - return values, self.is_group - - def overrides(self): - if not self.is_overriden and not self.child_overriden: - return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - if METADATA_KEY not in values: - values[METADATA_KEY] = {} - values[METADATA_KEY]["groups"] = groups - return values, self.is_group - class LabelWidget(QtWidgets.QWidget): is_input_type = False @@ -4016,7 +3836,7 @@ TypeToKlass.types["dict-invisible"] = DictWidget # --------------------------------------------- TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["path-widget"] = PathWidget -TypeToKlass.types["form"] = DictFormWidget +TypeToKlass.types["form"] = FormItemWidget TypeToKlass.types["label"] = LabelWidget TypeToKlass.types["separator"] = SplitterWidget From 943e3e997a3c6503083c31e3308937de6259c4b6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 18:48:15 +0100 Subject: [PATCH 168/219] initial commit of collapsable wrapper --- .../settings/settings/widgets/item_types.py | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 629ae415ac..6eb995eb6c 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3767,6 +3767,91 @@ class FormItemWidget(WrapperItemWidget): return item +class CollapsableWrapperItem(WrapperItemWidget): + def wrapper_initial_attributes(self, schema_data): + self.collapsable = schema_data.get("collapsable", True) + self.collapsed = schema_data.get("collapsed", True) + + def create_ui(self, label_widget=None): + content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("ContentWidget") + content_widget.setProperty("content_state", "") + + content_layout = QtWidgets.QGridLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + + body_widget = ExpandingWidget(self.schema_data["label"], self) + body_widget.set_content_widget(content_widget) + + label_widget = body_widget.label_widget + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + if not body_widget: + main_layout.addWidget(content_widget) + else: + main_layout.addWidget(body_widget) + + self.label_widget = label_widget + self.body_widget = body_widget + self.content_layout = content_layout + + if self.collapsable: + if not self.collapsed: + body_widget.toggle_content() + else: + body_widget.hide_toolbox(hide_content=False) + + for child_data in self.schema_data.get("children", []): + self.add_children_gui(child_data) + + any_visible = False + for input_field in self.input_fields: + if not input_field.hidden_by_role: + any_visible = True + break + + if not any_visible: + self.hide() + + def add_children_gui(self, child_configuration): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + row = self.content_layout.rowCount() + if not getattr(klass, "is_input_type", False): + item = klass(child_configuration, self) + self.content_layout.addWidget(item, row, 0, 1, 2) + return item + + label_widget = None + item = klass(child_configuration, self) + if not item.expand_in_grid: + label = child_configuration.get("label") + if label is not None: + label_widget = GridLabelWidget(label, self) + self.content_layout.addWidget(label_widget, row, 0, 1, 1) + + item.create_ui(label_widget=label_widget) + item.value_changed.connect(self._on_value_change) + + if label_widget: + if item.hidden_by_role: + label_widget.hide() + label_widget.input_field = item + self.content_layout.addWidget(item, row, 1, 1, 1) + else: + self.content_layout.addWidget(item, row, 0, 1, 2) + + self.input_fields.append(item) + return item + + def update_style(self): + """Update items styles.""" + return + + class LabelWidget(QtWidgets.QWidget): is_input_type = False @@ -3836,7 +3921,11 @@ TypeToKlass.types["dict-invisible"] = DictWidget # --------------------------------------------- TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["path-widget"] = PathWidget -TypeToKlass.types["form"] = FormItemWidget +# Wrappers +TypeToKlass.types["form"] = FormItemWidget +TypeToKlass.types["collapsable-wrap"] = CollapsableWrapperItem + +# UI items TypeToKlass.types["label"] = LabelWidget TypeToKlass.types["separator"] = SplitterWidget From 04d3a0e9cb57f17ecd9e408cada845972820e9a1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 19:03:44 +0100 Subject: [PATCH 169/219] item update styles --- .../settings/settings/widgets/item_types.py | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 6eb995eb6c..8ee2e6acb9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3620,6 +3620,7 @@ class WrapperItemWidget(QtWidgets.QWidget, SettingObject): self.value_changed.emit(self) if self.any_parent_is_group: self.hierarchical_style_update() + self.update_style() @property def child_has_studio_override(self): @@ -3847,9 +3848,39 @@ class CollapsableWrapperItem(WrapperItemWidget): self.input_fields.append(item) return item - def update_style(self): - """Update items styles.""" - return + def update_style(self, is_overriden=None): + child_has_studio_override = self.child_has_studio_override + child_modified = self.child_modified + child_invalid = self.child_invalid + child_state = self.style_state( + child_has_studio_override, + child_invalid, + self.child_overriden, + child_modified + ) + if child_state: + child_state = "child-{}".format(child_state) + + if child_state != self._child_state: + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + self._child_state = child_state + + state = self.style_state( + self.had_studio_override, + child_invalid, + self.is_overriden, + self.is_modified + ) + if self._state == state: + return + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state class LabelWidget(QtWidgets.QWidget): From 6b73060a384c7ab4f7c39afd240788c8638a22d1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 19:13:32 +0100 Subject: [PATCH 170/219] collapsible wrapper added to README and examples --- pype/tools/settings/settings/README.md | 25 ++++++++++++++++--- .../system_schema/example_schema.json | 10 ++++++++ .../settings/settings/widgets/item_types.py | 4 +-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index 046f903f90..4b75dc38a5 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -436,11 +436,9 @@ ## Proxy wrappers - should wraps multiple inputs only visually - these does not have `"key"` key and do not allow to have `"is_file"` or `"is_group"` modifiers enabled +- can't be used as widget (first item in e.g. `list`, `dict-modifiable`, etc.) ### form -- DEPRECATED - - may be used only in `dict` and `dict-invisible` where is currently used grid layout so form is not needed - - item is kept as still may be used in specific cases - wraps inputs into form look layout - should be used only for Pure inputs @@ -462,3 +460,24 @@ ] } ``` + + +### collapsible-wrap +- wraps inputs into collapsible widget + - looks like `dict` but does not hold `"key"` +- should be used only for Pure inputs + +``` +{ + "type": "collapsible-wrap", + "label": "Collapsible example" + "children": [ + { + "type": "text", + "key": "_example_input_collapsible", + "label": "Example input in collapsible wrapper" + }, { + ... + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index 00595fd3e4..9cbb214d86 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -418,6 +418,16 @@ ] } ] + }, { + "type": "collapsible-wrap", + "label": "Collapsible Wrapper without key", + "children": [ + { + "type": "text", + "key": "_example_input_collapsible", + "label": "Example input in collapsible wrapper" + } + ] } ] } diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8ee2e6acb9..013241d0cd 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3768,7 +3768,7 @@ class FormItemWidget(WrapperItemWidget): return item -class CollapsableWrapperItem(WrapperItemWidget): +class CollapsibleWrapperItem(WrapperItemWidget): def wrapper_initial_attributes(self, schema_data): self.collapsable = schema_data.get("collapsable", True) self.collapsed = schema_data.get("collapsed", True) @@ -3955,7 +3955,7 @@ TypeToKlass.types["path-widget"] = PathWidget # Wrappers TypeToKlass.types["form"] = FormItemWidget -TypeToKlass.types["collapsable-wrap"] = CollapsableWrapperItem +TypeToKlass.types["collapsible-wrap"] = CollapsibleWrapperItem # UI items TypeToKlass.types["label"] = LabelWidget From 0dacc5ca0e2f634f26184a4e8fe8d1ba43506004 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 1 Dec 2020 21:55:09 +0100 Subject: [PATCH 171/219] fix extract template and palettes thumbnails on mac --- pype/hosts/harmony/js/PypeHarmony.js | 6 +++--- pype/plugins/harmony/publish/extract_palette.py | 7 ++++++- pype/plugins/harmony/publish/extract_template.py | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pype/hosts/harmony/js/PypeHarmony.js b/pype/hosts/harmony/js/PypeHarmony.js index 57137d98c6..504bcc9ba2 100644 --- a/pype/hosts/harmony/js/PypeHarmony.js +++ b/pype/hosts/harmony/js/PypeHarmony.js @@ -183,11 +183,11 @@ PypeHarmony.color = function(rgba) { /** * get all dependencies for given node. * @function - * @param {string} node node path. + * @param {string} _node node path. * @return {array} List of dependent nodes. */ -PypeHarmony.getDependencies = function(node) { - var target_node = node; +PypeHarmony.getDependencies = function(_node) { + var target_node = _node; var numInput = node.numberOfInputPorts(target_node); var dependencies = []; for (var i = 0 ; i < numInput; i++) { diff --git a/pype/plugins/harmony/publish/extract_palette.py b/pype/plugins/harmony/publish/extract_palette.py index 0996079dea..029a4f0f11 100644 --- a/pype/plugins/harmony/publish/extract_palette.py +++ b/pype/plugins/harmony/publish/extract_palette.py @@ -38,7 +38,7 @@ class ExtractPalette(pype.api.Extractor): os.path.basename(palette_file) .split(".plt")[0] + "_swatches.png" ) - self.log.info(f"Temporary humbnail path {tmp_thumb_path}") + self.log.info(f"Temporary thumbnail path {tmp_thumb_path}") palette_version = str(instance.data.get("version")).zfill(3) @@ -52,6 +52,11 @@ class ExtractPalette(pype.api.Extractor): palette_version, palette_file, tmp_thumb_path) + except OSError as e: + # FIXME: this happens on Mac where PIL cannot access fonts + # for some reason. + self.log.warning("Thumbnail generation failed") + self.log.warning(e) except ValueError: self.log.error("Unsupported palette type for thumbnail.") diff --git a/pype/plugins/harmony/publish/extract_template.py b/pype/plugins/harmony/publish/extract_template.py index 06c4c1efcf..b8437c85ea 100644 --- a/pype/plugins/harmony/publish/extract_template.py +++ b/pype/plugins/harmony/publish/extract_template.py @@ -31,7 +31,11 @@ class ExtractTemplate(pype.api.Extractor): for backdrop in self.get_backdrops(dependency): backdrops[backdrop["title"]["text"]] = backdrop unique_backdrops = [backdrops[x] for x in set(backdrops.keys())] - + if not unique_backdrops: + self.log.error(("No backdrops detected for template. " + "Please move template instance node onto " + "some backdrop and try again.")) + raise AssertionError("No backdrop detected") # Get non-connected nodes within backdrops. all_nodes = instance.context.data.get("allNodes") for node in [x for x in all_nodes if x not in dependencies]: From cabf5584958874d350a91411d2437d16df9c4466 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 1 Dec 2020 23:08:41 +0100 Subject: [PATCH 172/219] tweaks to harmony and blender launchers --- .../defaults/system_settings/applications.json | 14 ++++++++++++-- .../host_settings/schema_harmony.json | 8 -------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index cce4c4f25e..e5cd249ffe 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -827,7 +827,12 @@ "variant_label": "2.90", "icon": "", "executables": { - "windows": [], + "windows": [ + [ + "C:\\Program Files\\Blender Foundation\\Blender 2.90\\blender.exe", + "" + ] + ], "darwin": [], "linux": [] }, @@ -843,7 +848,12 @@ "variant_label": "2.83", "icon": "", "executables": { - "windows": [], + "windows": [ + [ + "C:\\Program Files\\Blender Foundation\\Blender 2.83\\blender.exe", + "" + ] + ], "darwin": [], "linux": [] }, diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_harmony.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_harmony.json index d7b9e61bda..8ca793f90b 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_harmony.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/schema_harmony.json @@ -30,14 +30,6 @@ "host_version": "20", "host_name": "harmony" }, - { - "host_version": "19", - "host_name": "harmony" - }, - { - "host_version": "18", - "host_name": "harmony" - }, { "host_version": "17", "host_name": "harmony" From e8947ebb7fe504fb90175acf8c9fa615250ee113 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 1 Dec 2020 23:58:25 +0100 Subject: [PATCH 173/219] fix last workfile quotes in hooks --- pype/hooks/hiero/pre_launch_args.py | 2 +- pype/hooks/maya/pre_launch_args.py | 2 +- pype/hooks/nukestudio/pre_launch_args.py | 2 +- pype/hooks/nukex/pre_launch_args.py | 2 +- pype/hooks/tvpaint/pre_launch_args.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index feca6dc3eb..754f822759 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -13,5 +13,5 @@ class HieroLaunchArguments(PreLaunchHook): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): self.launch_context.launch_args.append( - "\"{}\"".format(last_workfile) + "{}.format(last_workfile) ) diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py index 8b37bac15b..c2b0d75751 100644 --- a/pype/hooks/maya/pre_launch_args.py +++ b/pype/hooks/maya/pre_launch_args.py @@ -14,5 +14,5 @@ class MayaLaunchArguments(PreLaunchHook): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): self.launch_context.launch_args.append( - "\"{}\"".format(last_workfile) + "{}".format(last_workfile) ) diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index e572ca32a2..5a8cd7b297 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -13,5 +13,5 @@ class NukeStudioLaunchArguments(PreLaunchHook): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): self.launch_context.launch_args.append( - "\"{}\"".format(last_workfile) + "{}".format(last_workfile) ) diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index f0e5cf7733..911346ec12 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -13,5 +13,5 @@ class NukeXLaunchArguments(PreLaunchHook): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): self.launch_context.launch_args.append( - "\"{}\"".format(last_workfile) + "{}".format(last_workfile) ) diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index 13ec320fa0..e277656af8 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -37,7 +37,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): workfile_path = self.workfile_path() if workfile_path: new_launch_args.append( - "\"{}\"".format(workfile_path) + "{}".format(workfile_path) ) # How to create new command line From eff81178816f0aa423c7911b54a13447568e7d80 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 1 Dec 2020 23:58:44 +0100 Subject: [PATCH 174/219] simplify application labels --- .../system_settings/applications.json | 52 ++++--------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index e5cd249ffe..b0e9635ca8 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -1,7 +1,7 @@ { "maya": { "enabled": true, - "label": "Autodesk Maya", + "label": "Maya", "icon": "{}/app_icons/maya.png", "host_name": "maya", "environment": { @@ -123,7 +123,7 @@ }, "mayabatch": { "enabled": true, - "label": "Autodesk MayaBatch", + "label": "MayaBatch", "icon": "{}/app_icons/maya.png", "host_name": "maya", "environment": { @@ -621,7 +621,7 @@ }, "fusion": { "enabled": true, - "label": "BlackMagic Fusion", + "label": "Fusion", "icon": "{}/app_icons/fusion.png", "host_name": "fusion", "environment": { @@ -666,7 +666,7 @@ }, "resolve": { "enabled": true, - "label": "Blackmagic DaVinci Resolve", + "label": "Resolve", "icon": "{}/app_icons/resolve.png", "host_name": "resolve", "environment": { @@ -744,7 +744,7 @@ }, "houdini": { "enabled": true, - "label": "SideFX Houdini", + "label": "Houdini", "icon": "{}/app_icons/houdini.png", "host_name": "houdini", "environment": { @@ -867,7 +867,7 @@ }, "harmony": { "enabled": true, - "label": "Toon Boom Harmony", + "label": "Harmony", "icon": "{}/app_icons/harmony.png", "host_name": "harmony", "environment": { @@ -897,38 +897,6 @@ } } }, - "harmony_19": { - "enabled": true, - "label": "", - "variant_label": "19", - "icon": "", - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "__environment_keys__": { - "harmony_19": [] - } - } - }, - "harmony_18": { - "enabled": true, - "label": "", - "variant_label": "18", - "icon": "", - "executables": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "__environment_keys__": { - "harmony_18": [] - } - } - }, "harmony_17": { "enabled": true, "label": "", @@ -969,7 +937,7 @@ "tvpaint_Animation 11 (64bits)": { "enabled": true, "label": "", - "variant_label": "Animation 11 (64bits)", + "variant_label": "11 (64bits)", "icon": "", "executables": { "windows": [ @@ -990,7 +958,7 @@ "tvpaint_Animation 11 (32bits)": { "enabled": true, "label": "", - "variant_label": "Animation 11 (32bits)", + "variant_label": "11 (32bits)", "icon": "", "executables": { "windows": [ @@ -1012,7 +980,7 @@ }, "photoshop": { "enabled": true, - "label": "Adobe Photoshop", + "label": "Photoshop", "icon": "{}/app_icons/photoshop.png", "host_name": "photoshop", "environment": { @@ -1078,7 +1046,7 @@ }, "aftereffects": { "enabled": true, - "label": "Adobe AfterEffects", + "label": "AfterEffects", "icon": "{}/app_icons/aftereffects.png", "host_name": "aftereffects", "environment": { From a10bd5d55e67e2c8ce8368f5acf388c14d4b412b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 09:19:45 +0100 Subject: [PATCH 175/219] fixed syntax error and simplified workfile argument --- pype/hooks/celaction/pre_celaction_registers.py | 4 +--- pype/hooks/hiero/pre_launch_args.py | 4 +--- pype/hooks/maya/pre_launch_args.py | 4 +--- pype/hooks/nukestudio/pre_launch_args.py | 4 +--- pype/hooks/nukex/pre_launch_args.py | 4 +--- pype/hooks/tvpaint/pre_launch_args.py | 4 +--- 6 files changed, 6 insertions(+), 18 deletions(-) diff --git a/pype/hooks/celaction/pre_celaction_registers.py b/pype/hooks/celaction/pre_celaction_registers.py index 3f9d81fb98..04ecf82c5c 100644 --- a/pype/hooks/celaction/pre_celaction_registers.py +++ b/pype/hooks/celaction/pre_celaction_registers.py @@ -20,9 +20,7 @@ class CelactionPrelaunchHook(PreLaunchHook): # Add workfile path to launch arguments workfile_path = self.workfile_path() if workfile_path: - self.launch_context.launch_args.append( - "\"{}\"".format(workfile_path) - ) + self.launch_context.launch_args.append(workfile_path) project_name = self.data["project_name"] asset_name = self.data["asset_name"] diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py index 754f822759..6f5d0c0b00 100644 --- a/pype/hooks/hiero/pre_launch_args.py +++ b/pype/hooks/hiero/pre_launch_args.py @@ -12,6 +12,4 @@ class HieroLaunchArguments(PreLaunchHook): if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): - self.launch_context.launch_args.append( - "{}.format(last_workfile) - ) + self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py index c2b0d75751..26b935ea01 100644 --- a/pype/hooks/maya/pre_launch_args.py +++ b/pype/hooks/maya/pre_launch_args.py @@ -13,6 +13,4 @@ class MayaLaunchArguments(PreLaunchHook): if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): - self.launch_context.launch_args.append( - "{}".format(last_workfile) - ) + self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py index 5a8cd7b297..6056441042 100644 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ b/pype/hooks/nukestudio/pre_launch_args.py @@ -12,6 +12,4 @@ class NukeStudioLaunchArguments(PreLaunchHook): if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): - self.launch_context.launch_args.append( - "{}".format(last_workfile) - ) + self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py index 911346ec12..979bfcce0b 100644 --- a/pype/hooks/nukex/pre_launch_args.py +++ b/pype/hooks/nukex/pre_launch_args.py @@ -12,6 +12,4 @@ class NukeXLaunchArguments(PreLaunchHook): if self.data.get("start_last_workfile"): last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): - self.launch_context.launch_args.append( - "{}".format(last_workfile) - ) + self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/tvpaint/pre_launch_args.py b/pype/hooks/tvpaint/pre_launch_args.py index e277656af8..210b1e99c3 100644 --- a/pype/hooks/tvpaint/pre_launch_args.py +++ b/pype/hooks/tvpaint/pre_launch_args.py @@ -36,9 +36,7 @@ class TvpaintPrelaunchHook(PreLaunchHook): # Add workfile to launch arguments workfile_path = self.workfile_path() if workfile_path: - new_launch_args.append( - "{}".format(workfile_path) - ) + new_launch_args.append(workfile_path) # How to create new command line # if platform.system().lower() == "windows": From 5bb86beee906e8b962addc71847c4daab360a15f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:01:27 +0100 Subject: [PATCH 176/219] fixed add button in modifiable dictionary --- .../tools/settings/settings/widgets/item_types.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e8a7397d9f..64b4fe32e6 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2040,15 +2040,16 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_label_input.focusOutEvent = key_label_input_focused_out spacer_widget = None + add_btn = None if not self.collapsable_key: spacer_widget = QtWidgets.QWidget(self) spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) spacer_widget.setVisible(False) - add_btn = QtWidgets.QPushButton("+") - add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - add_btn.setProperty("btn-type", "tool-item") - add_btn.setFixedSize(self._btn_size, self._btn_size) + add_btn = QtWidgets.QPushButton("+") + add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + add_btn.setProperty("btn-type", "tool-item") + add_btn.setFixedSize(self._btn_size, self._btn_size) edit_btn = None if self.collapsable_key: @@ -2069,7 +2070,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): if self.collapsable_key: key_input_label_widget = QtWidgets.QLabel("Key:") key_label_input_label_widget = QtWidgets.QLabel("Label:") - wrapper_widget.add_widget_before_label(add_btn) wrapper_widget.add_widget_before_label(edit_btn) wrapper_widget.add_widget_after_label(key_input_label_widget) wrapper_widget.add_widget_after_label(key_input) @@ -2093,8 +2093,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): key_label_input.returnPressed.connect(self._on_enter_press) value_input.value_changed.connect(self._on_value_change) - - add_btn.clicked.connect(self.on_add_clicked) + if add_btn: + add_btn.clicked.connect(self.on_add_clicked) if edit_btn: edit_btn.clicked.connect(self.on_edit_pressed) remove_btn.clicked.connect(self.on_remove_clicked) @@ -2283,7 +2283,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._is_empty = is_empty self.value_input.setVisible(not is_empty) - self.add_btn.setVisible(is_empty) if not self.collapsable_key: self.key_input.setVisible(not is_empty) self.remove_btn.setEnabled(not is_empty) From 0a253f96e0fd948429e19cf79db9a89ca3f18292 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:01:53 +0100 Subject: [PATCH 177/219] modifiable dict cna handle required keys --- .../settings/settings/widgets/item_types.py | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 64b4fe32e6..8bbbb0e9c9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2422,6 +2422,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): ) self.hightlight_content = schema_data.get("highlight_content") or False self.collapsable_key = schema_data.get("collapsable_key") or False + self.required_keys = schema_data.get("required_keys") or [] object_type = schema_data["object_type"] if isinstance(object_type, dict): @@ -2503,7 +2504,14 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.setAttribute(QtCore.Qt.WA_TranslucentBackground) - self.add_row(is_empty=True) + last_required_item = None + for key in self.required_keys: + last_required_item = self.add_row(key=key, is_required=True) + + if last_required_item: + last_required_item.set_as_last_required() + else: + self.add_row(is_empty=True) def count(self): return len(self.input_fields) @@ -2513,10 +2521,17 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): metadata = value.get(METADATA_KEY, {}) dynamic_key_labels = metadata.get("dynamic_key_label") or {} - previous_inputs = tuple(self.input_fields) + + required_items = list(self.required_inputs_by_key.values()) + previous_inputs = list() + for input_field in self.input_fields: + if input_field not in required_items: + previous_inputs.append(input_field) + for item_key, item_value in value.items(): if item_key is METADATA_KEY: continue + label = dynamic_key_labels.get(item_key) self.add_row(key=item_key, label=label, value=item_value) @@ -2648,9 +2663,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def config_value(self): return {self.key: self.item_value_with_metadata()} - def add_row( - self, row=None, key=None, label=None, value=None, is_empty=False - ): + def _create_item(self, row, key, is_empty, is_required): # Create new item item_widget = ModifiableDictItem( self.item_schema, self, self.content_widget @@ -2658,6 +2671,10 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if is_empty: item_widget.set_as_empty() + if is_required: + item_widget.set_as_required(key) + self.required_inputs_by_key[key] = item_widget + item_widget.value_changed.connect(self._on_value_change) if row is None: @@ -2686,6 +2703,20 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.setTabOrder( input_field.key_input, previous_input ) + return item_widget + + def add_row( + self, + row=None, + key=None, + label=None, + value=None, + is_empty=False, + is_required=False + ): + item_widget = self.required_inputs_by_key.get(key) + if not item_widget: + item_widget = self._create_item(row, key, is_empty, is_required) # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` @@ -2701,6 +2732,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._on_value_change() self.parent().updateGeometry() + return item_widget + def remove_row(self, item_widget): item_widget.value_changed.disconnect() From 751e6afe70abc14a58bec0bd6bd6a8d5e804f1a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:02:15 +0100 Subject: [PATCH 178/219] modified modifiable dict item to handle required keys --- .../settings/settings/widgets/item_types.py | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8bbbb0e9c9..063262c43f 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1988,6 +1988,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._is_empty = False self._is_key_duplicated = False + self._is_required = False + self.origin_key = NOT_SET self.origin_key_label = NOT_SET @@ -2161,6 +2163,24 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._on_focus_lose() self.update_style() + def set_as_required(self, key): + self.key_input.setText(key) + self.key_input.setEnabled(False) + self._is_required = True + + if self._is_empty: + self.set_as_empty(False) + + if self.collapsable_key: + self.remove_btn.setVisible(False) + else: + self.remove_btn.setEnabled(False) + self.add_btn.setEnabled(False) + + def set_as_last_required(self): + if self.add_btn: + self.add_btn.setEnabled(True) + def _on_focus_lose(self): if ( self.edit_btn.hasFocus() @@ -2272,9 +2292,13 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.key_input.setVisible(enabled) self.key_input_label_widget.setVisible(enabled) self.key_label_input.setVisible(enabled) - self.remove_btn.setVisible(enabled) + if not self._is_required: + self.remove_btn.setVisible(enabled) if enabled: - self.key_input.setFocus() + if self.key_input.isEnabled(): + self.key_input.setFocus() + else: + self.key_label_input.setFocus() def on_remove_clicked(self): self._parent.remove_row(self) @@ -2414,6 +2438,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.initial_attributes(schema_data, parent, as_widget) self.input_fields = [] + self.required_inputs_by_key = {} # Validation of "key" key self.key = schema_data["key"] From be2434b01e11a0bcf1a02df0cc5f9bfc519f7ec0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:33:11 +0100 Subject: [PATCH 179/219] loader do not use "objectName" key to discover right container --- pype/plugins/tvpaint/load/load_reference_image.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/plugins/tvpaint/load/load_reference_image.py b/pype/plugins/tvpaint/load/load_reference_image.py index b3cefee4c3..e0e8885d57 100644 --- a/pype/plugins/tvpaint/load/load_reference_image.py +++ b/pype/plugins/tvpaint/load/load_reference_image.py @@ -104,6 +104,7 @@ class LoadImage(pipeline.Loader): def _remove_layers(self, layer_ids, layers=None): if not layer_ids: + self.log.warning("Got empty layer ids list.") return if layers is None: @@ -117,6 +118,7 @@ class LoadImage(pipeline.Loader): layer_ids_to_remove.append(layer_id) if not layer_ids_to_remove: + self.log.warning("No layers to delete.") return george_script_lines = [] @@ -128,12 +130,14 @@ class LoadImage(pipeline.Loader): def remove(self, container): layer_ids = self.layer_ids_from_container(container) + self.log.warning("Layers to delete {}".format(layer_ids)) self._remove_layers(layer_ids) current_containers = pipeline.ls() pop_idx = None for idx, cur_con in enumerate(current_containers): - if cur_con["objectName"] == container["objectName"]: + cur_con_layer_ids = self.layer_ids_from_container(cur_con) + if cur_con_layer_ids == layer_ids: pop_idx = idx break From 0785e1b032913248281245e094f9b407502ba5b0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:46:44 +0100 Subject: [PATCH 180/219] updated readme --- pype/tools/settings/settings/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index 4b75dc38a5..53f21aad06 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -310,6 +310,9 @@ - items in this input can be removed and added same way as in `list` input - value items in dictionary must be the same type - type of items is defined with key `"object_type"` +- required keys may be defined under `"required_keys"` + - required keys must be defined as a list (e.g. `["key_1"]`) and are moved to the top + - these keys can't be removed or edited (it is possible to edit label if item is collapsible) - there are 2 possible ways how to set the type: 1.) dictionary with item modifiers (`number` input has `minimum`, `maximum` and `decimals`) in that case item type must be set as value of `"type"` (example below) 2.) item type name as string without modifiers (e.g. `text`) From 9576c46c2166893d41484778abb89c382f43af43 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 12:57:11 +0100 Subject: [PATCH 181/219] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index e199aa5550..abe7e03a96 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.14.1" +__version__ = "2.14.2" From bc75ef7563543909957124c4d23a83ee92b7ebd3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 17:03:56 +0100 Subject: [PATCH 182/219] implemented base for settings category SettingsCategoryWidget --- pype/tools/settings/settings/widgets/base.py | 275 +++++++++++++++++++ 1 file changed, 275 insertions(+) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 2e5c2f356b..11f973a91f 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -33,6 +33,281 @@ from avalon import io from avalon.vendor import qtawesome +class SettingsCategoryWidget(QtWidgets.QWidget): + schema_category = None + initial_schema_name = None + + def __init__(self, user_role, parent=None): + super(SettingsCategoryWidget, self).__init__(parent) + + self.user_role = user_role + + self.initialize_attributes() + self.create_ui() + self.reset() + + def initialize_attributes(self): + self._hide_studio_overrides = False + self._ignore_value_changes = False + + self.keys = [] + self.input_fields = [] + self.schema = None + self.main_schema_key = None + + # Required attributes for items + self.is_overidable = False + self._has_studio_override = False + self._is_overriden = False + self._as_widget = False + self._is_group = False + self._any_parent_as_widget = False + self._any_parent_is_group = False + self.has_studio_override = self._has_studio_override + self.is_overriden = self._is_overriden + self.as_widget = self._as_widget + self.is_group = self._as_widget + self.any_parent_as_widget = self._any_parent_as_widget + self.any_parent_is_group = self._any_parent_is_group + + def create_ui(self): + scroll_widget = QtWidgets.QScrollArea(self) + scroll_widget.setObjectName("GroupWidget") + content_widget = QtWidgets.QWidget(scroll_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 3, 3) + content_layout.setSpacing(0) + content_layout.setAlignment(QtCore.Qt.AlignTop) + + scroll_widget.setWidgetResizable(True) + scroll_widget.setWidget(content_widget) + + footer_widget = QtWidgets.QWidget() + footer_layout = QtWidgets.QHBoxLayout(footer_widget) + + if self.user_role == "developer": + self._add_developer_ui(footer_layout) + + save_btn = QtWidgets.QPushButton("Save") + spacer_widget = QtWidgets.QWidget() + footer_layout.addWidget(spacer_widget, 1) + footer_layout.addWidget(save_btn, 0) + + configurations_widget = QtWidgets.QWidget() + configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) + configurations_layout.setContentsMargins(0, 0, 0, 0) + configurations_layout.setSpacing(0) + + configurations_layout.addWidget(scroll_widget, 1) + configurations_layout.addWidget(footer_widget, 0) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + main_layout.addWidget(configurations_widget, 1) + + save_btn.clicked.connect(self._save) + + self.scroll_widget = scroll_widget + self.content_layout = content_layout + self.content_widget = content_widget + self.configurations_widget = configurations_widget + self.main_layout = main_layout + + self.ui_tweaks() + + def ui_tweaks(self): + return + + def _add_developer_ui(self, footer_layout): + save_as_default_btn = QtWidgets.QPushButton("Save as Default") + + refresh_icon = qtawesome.icon("fa.refresh", color="white") + refresh_button = QtWidgets.QPushButton() + refresh_button.setIcon(refresh_icon) + + hide_studio_overrides = QtWidgets.QCheckBox() + hide_studio_overrides.setChecked(self._hide_studio_overrides) + + hide_studio_overrides_widget = QtWidgets.QWidget() + hide_studio_overrides_layout = QtWidgets.QHBoxLayout( + hide_studio_overrides_widget + ) + _label_widget = QtWidgets.QLabel( + "Hide studio overrides", hide_studio_overrides_widget + ) + hide_studio_overrides_layout.addWidget(_label_widget) + hide_studio_overrides_layout.addWidget(hide_studio_overrides) + + footer_layout.addWidget(save_as_default_btn, 0) + footer_layout.addWidget(refresh_button, 0) + footer_layout.addWidget(hide_studio_overrides_widget, 0) + + save_as_default_btn.clicked.connect(self._save_as_defaults) + refresh_button.clicked.connect(self._on_refresh) + hide_studio_overrides.stateChanged.connect( + self._on_hide_studio_overrides + ) + + def save(self): + """Save procedure.""" + raise NotImplementedError("Method `save` is not implemented.") + + def defaults_dir(self): + """Path to defaults folder.""" + raise NotImplementedError("Method `defaults_dir` is not implemented.") + + def update_values(self): + """Procedure of update values of items on context change or reset.""" + raise NotImplementedError("Method `update_values` is not implemented.") + + def validate_defaults_to_save(self, value): + raise NotImplementedError( + "Method `validate_defaults_to_save` not implemented." + ) + + def any_parent_overriden(self): + return False + + @property + def ignore_value_changes(self): + return self._ignore_value_changes + + @ignore_value_changes.setter + def ignore_value_changes(self, value): + self._ignore_value_changes = value + if value is False: + self.hierarchical_style_update() + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + + def reset(self): + reset_default_settings() + + self.keys.clear() + self.input_fields.clear() + while self.content_layout.count() != 0: + widget = self.content_layout.itemAt(0).widget() + self.content_layout.removeWidget(widget) + widget.deleteLater() + + self.schema = lib.gui_schema( + self.schema_category, self.initial_schema_name + ) + + self.main_schema_key = self.schema["key"] + + self.add_children_gui(self.schema) + self._update_values() + self.hierarchical_style_update() + + def items_are_valid(self): + has_invalid = False + for item in self.input_fields: + if item.child_invalid: + has_invalid = True + + if not has_invalid: + return True + + invalid_items = [] + for item in self.input_fields: + invalid_items.extend(item.get_invalid()) + msg_box = QtWidgets.QMessageBox( + QtWidgets.QMessageBox.Warning, + "Invalid input", + "There is invalid value in one of inputs." + " Please lead red color and fix them." + ) + msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) + msg_box.exec_() + + first_invalid_item = invalid_items[0] + self.scroll_widget.ensureWidgetVisible(first_invalid_item) + if first_invalid_item.isVisible(): + first_invalid_item.setFocus(True) + return False + + def _save(self): + if not self.items_are_valid(): + return + + self.save() + + self._update_values() + + def _on_refresh(self): + self.reset() + + def _on_hide_studio_overrides(self, state): + self._hide_studio_overrides = (state == QtCore.Qt.Checked) + self._update_values() + self.hierarchical_style_update() + + def _save_as_defaults(self): + if not self.items_are_valid(): + return + + all_values = {} + for item in self.input_fields: + all_values.update(item.config_value()) + + for key in reversed(self.keys): + all_values = {key: all_values} + + # Skip first key and convert data to store + all_values = lib.convert_gui_data_with_metadata( + all_values[self.main_schema_key] + ) + + if not self.validate_defaults_to_save(all_values): + return + + defaults_dir = self.defaults_dir() + keys_to_file = lib.file_keys_from_schema(self.schema) + for key_sequence in keys_to_file: + # Skip first key + key_sequence = key_sequence[1:] + subpath = "/".join(key_sequence) + ".json" + + new_values = all_values + for key in key_sequence: + new_values = new_values[key] + + output_path = os.path.join(defaults_dir, subpath) + dirpath = os.path.dirname(output_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving data to: ", subpath) + with open(output_path, "w") as file_stream: + json.dump(new_values, file_stream, indent=4) + + reset_default_settings() + + self._update_values() + self.hierarchical_style_update() + + def _update_values(self): + self.ignore_value_changes = True + self.update_values() + self.ignore_value_changes = False + + def add_children_gui(self, child_configuration): + klass = lib.TypeToKlass.types.get(child_configuration["type"]) + item = klass(child_configuration, self) + item.create_ui() + self.input_fields.append(item) + self.content_layout.addWidget(item, 0) + + # Add spacer to stretch children guis + self.content_layout.addWidget( + QtWidgets.QWidget(self.content_widget), 1 + ) + + class SystemWidget(QtWidgets.QWidget): is_overidable = False has_studio_override = _has_studio_override = False From 58349615c73c7bdb2b939fac945884a180006219 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 17:05:29 +0100 Subject: [PATCH 183/219] SystemWidget is using new SettingsCategory as base --- pype/tools/settings/settings/widgets/base.py | 240 ++----------------- 1 file changed, 21 insertions(+), 219 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 11f973a91f..ba5868737a 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -308,153 +308,17 @@ class SettingsCategoryWidget(QtWidgets.QWidget): ) -class SystemWidget(QtWidgets.QWidget): - is_overidable = False - has_studio_override = _has_studio_override = False - is_overriden = _is_overriden = False - as_widget = _as_widget = False - any_parent_as_widget = _any_parent_as_widget = False - is_group = _is_group = False - any_parent_is_group = _any_parent_is_group = False +class SystemWidget(SettingsCategoryWidget): + schema_category = "system_schema" + initial_schema_name = "schema_main" - def __init__(self, user_role, parent=None): - super(SystemWidget, self).__init__(parent) - - self.user_role = user_role - self._hide_studio_overrides = False - self._ignore_value_changes = False - - self.input_fields = [] + def initialize_attributes(self): self.environ_fields = [] - - scroll_widget = QtWidgets.QScrollArea(self) - scroll_widget.setObjectName("GroupWidget") - content_widget = QtWidgets.QWidget(scroll_widget) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 3, 3) - content_layout.setSpacing(0) - content_layout.setAlignment(QtCore.Qt.AlignTop) - content_widget.setLayout(content_layout) - - scroll_widget.setWidgetResizable(True) - scroll_widget.setWidget(content_widget) - - self.scroll_widget = scroll_widget - self.content_layout = content_layout - self.content_widget = content_widget - - footer_widget = QtWidgets.QWidget() - footer_layout = QtWidgets.QHBoxLayout(footer_widget) - - if self.user_role == "developer": - save_as_default_btn = QtWidgets.QPushButton("Save as Default") - save_as_default_btn.clicked.connect(self._save_as_defaults) - - refresh_icon = qtawesome.icon("fa.refresh", color="white") - refresh_button = QtWidgets.QPushButton() - refresh_button.setIcon(refresh_icon) - refresh_button.clicked.connect(self._on_refresh) - - hide_studio_overrides = QtWidgets.QCheckBox() - hide_studio_overrides.setChecked(self._hide_studio_overrides) - hide_studio_overrides.stateChanged.connect( - self._on_hide_studio_overrides - ) - - hide_studio_overrides_widget = QtWidgets.QWidget() - hide_studio_overrides_layout = QtWidgets.QHBoxLayout( - hide_studio_overrides_widget - ) - _label_widget = QtWidgets.QLabel( - "Hide studio overrides", hide_studio_overrides_widget - ) - hide_studio_overrides_layout.addWidget(_label_widget) - hide_studio_overrides_layout.addWidget(hide_studio_overrides) - - footer_layout.addWidget(save_as_default_btn, 0) - footer_layout.addWidget(refresh_button, 0) - footer_layout.addWidget(hide_studio_overrides_widget, 0) - - save_btn = QtWidgets.QPushButton("Save") - spacer_widget = QtWidgets.QWidget() - footer_layout.addWidget(spacer_widget, 1) - footer_layout.addWidget(save_btn, 0) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - self.setLayout(layout) - - layout.addWidget(scroll_widget, 1) - layout.addWidget(footer_widget, 0) - - save_btn.clicked.connect(self._save) - - self.reset() - - def any_parent_overriden(self): - return False - - @property - def ignore_value_changes(self): - return self._ignore_value_changes - - @ignore_value_changes.setter - def ignore_value_changes(self, value): - self._ignore_value_changes = value - if value is False: - self.hierarchical_style_update() - - def hierarchical_style_update(self): - for input_field in self.input_fields: - input_field.hierarchical_style_update() + super(SystemWidget, self).initialize_attributes() def add_environ_field(self, input_field): self.environ_fields.append(input_field) - def reset(self): - reset_default_settings() - - self.input_fields.clear() - self.environ_fields.clear() - while self.content_layout.count() != 0: - widget = self.content_layout.itemAt(0).widget() - self.content_layout.removeWidget(widget) - widget.deleteLater() - - self.schema = lib.gui_schema("system_schema", "schema_main") - self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema) - self._update_values() - self.hierarchical_style_update() - - def items_are_valid(self): - has_invalid = False - for item in self.input_fields: - if item.child_invalid: - has_invalid = True - - if not has_invalid: - return True - - invalid_items = [] - for item in self.input_fields: - invalid_items.extend(item.get_invalid()) - msg_box = QtWidgets.QMessageBox( - QtWidgets.QMessageBox.Warning, - "Invalid input", - "There is invalid value in one of inputs." - " Please lead red color and fix them." - ) - msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) - msg_box.exec_() - - first_invalid_item = invalid_items[0] - self.scroll_widget.ensureWidgetVisible(first_invalid_item) - if first_invalid_item.isVisible(): - first_invalid_item.setFocus(True) - return False - def duplicated_env_group_validation(self, values=None, overrides=None): try: if overrides is not None: @@ -483,83 +347,35 @@ class SystemWidget(QtWidgets.QWidget): return False return True - def _save(self): - if not self.items_are_valid(): - return + def defaults_dir(self): + return os.path.join(DEFAULTS_DIR, SYSTEM_SETTINGS_KEY) + def validate_defaults_to_save(self, values): + return self.duplicated_env_group_validation(values) + + def reset(self): + self.environ_fields.clear() + super(SystemWidget, self).reset() + + def save(self, all_values): _data = {} for input_field in self.input_fields: value, _is_group = input_field.studio_overrides() if value is not lib.NOT_SET: _data.update(value) - values = lib.convert_gui_data_to_overrides(_data.get("system", {})) + values = lib.convert_gui_data_to_overrides( + _data.get(self.main_schema_key, {}) + ) if not self.duplicated_env_group_validation(overrides=values): return save_studio_settings(values) - self._update_values() - - def _on_refresh(self): - self.reset() - - def _on_hide_studio_overrides(self, state): - self._hide_studio_overrides = (state == QtCore.Qt.Checked) - self._update_values() - self.hierarchical_style_update() - - def _save_as_defaults(self): - if not self.items_are_valid(): - return - - all_values = {} - for item in self.input_fields: - all_values.update(item.config_value()) - - for key in reversed(self.keys): - _all_values = {key: all_values} - all_values = _all_values - - # Skip first key - all_values = lib.convert_gui_data_with_metadata(all_values["system"]) - - if not self.duplicated_env_group_validation(all_values): - return - - prject_defaults_dir = os.path.join( - DEFAULTS_DIR, SYSTEM_SETTINGS_KEY - ) - keys_to_file = lib.file_keys_from_schema(self.schema) - for key_sequence in keys_to_file: - # Skip first key - key_sequence = key_sequence[1:] - subpath = "/".join(key_sequence) + ".json" - - new_values = all_values - for key in key_sequence: - new_values = new_values[key] - - output_path = os.path.join(prject_defaults_dir, subpath) - dirpath = os.path.dirname(output_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to: ", subpath) - with open(output_path, "w") as file_stream: - json.dump(new_values, file_stream, indent=4) - - reset_default_settings() - - self._update_values() - self.hierarchical_style_update() - - def _update_values(self): - self.ignore_value_changes = True - + def update_values(self): default_values = lib.convert_data_to_gui_data({ - "system": default_settings()[SYSTEM_SETTINGS_KEY] + self.main_schema_key: default_settings()[SYSTEM_SETTINGS_KEY] }) for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -568,26 +384,12 @@ class SystemWidget(QtWidgets.QWidget): system_values = lib.NOT_SET else: system_values = lib.convert_overrides_to_gui_data( - {"system": studio_system_settings()} + {self.main_schema_key: studio_system_settings()} ) for input_field in self.input_fields: input_field.update_studio_values(system_values) - self.ignore_value_changes = False - - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - klass = lib.TypeToKlass.types.get(item_type) - item = klass(child_configuration, self) - item.create_ui() - self.input_fields.append(item) - self.content_layout.addWidget(item, 0) - - # Add spacer to stretch children guis - spacer = QtWidgets.QWidget(self.content_widget) - self.content_layout.addWidget(spacer, 1) - class ProjectListView(QtWidgets.QListView): left_mouse_released_at = QtCore.Signal(QtCore.QModelIndex) From 9f992d4f890628dec24c05d04ab45e699018be4a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 17:06:01 +0100 Subject: [PATCH 184/219] ProjectWidget is also using setting category as base --- pype/tools/settings/settings/widgets/base.py | 251 ++----------------- 1 file changed, 25 insertions(+), 226 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index ba5868737a..c1ff640837 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -517,145 +517,30 @@ class ProjectListWidget(QtWidgets.QWidget): ) -class ProjectWidget(QtWidgets.QWidget): - has_studio_override = _has_studio_override = False - is_overriden = _is_overriden = False - as_widget = _as_widget = False - any_parent_as_widget = _any_parent_as_widget = False - is_group = _is_group = False - any_parent_is_group = _any_parent_is_group = False +class ProjectWidget(SettingsCategoryWidget): + schema_category = "projects_schema" + initial_schema_name = "schema_main" - def __init__(self, user_role, parent=None): - super(ProjectWidget, self).__init__(parent) - - self.user_role = user_role - self._hide_studio_overrides = False - - self.is_overidable = False - self._ignore_value_changes = False + def initialize_attributes(self): self.project_name = None - self.input_fields = [] - - scroll_widget = QtWidgets.QScrollArea(self) - scroll_widget.setObjectName("GroupWidget") - content_widget = QtWidgets.QWidget(scroll_widget) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 3, 3) - content_layout.setSpacing(0) - content_layout.setAlignment(QtCore.Qt.AlignTop) - content_widget.setLayout(content_layout) - - scroll_widget.setWidgetResizable(True) - scroll_widget.setWidget(content_widget) + super(ProjectWidget, self).initialize_attributes() + def ui_tweaks(self): project_list_widget = ProjectListWidget(self) - content_layout.addWidget(project_list_widget) - footer_widget = QtWidgets.QWidget() - footer_layout = QtWidgets.QHBoxLayout(footer_widget) + self.main_layout.insertWidget(0, project_list_widget, 0) - if self.user_role == "developer": - save_as_default_btn = QtWidgets.QPushButton("Save as Default") - save_as_default_btn.clicked.connect(self._save_as_defaults) - - refresh_icon = qtawesome.icon("fa.refresh", color="white") - refresh_button = QtWidgets.QPushButton() - refresh_button.setIcon(refresh_icon) - refresh_button.clicked.connect(self._on_refresh) - - hide_studio_overrides = QtWidgets.QCheckBox() - hide_studio_overrides.setChecked(self._hide_studio_overrides) - hide_studio_overrides.stateChanged.connect( - self._on_hide_studio_overrides - ) - - hide_studio_overrides_widget = QtWidgets.QWidget() - hide_studio_overrides_layout = QtWidgets.QHBoxLayout( - hide_studio_overrides_widget - ) - _label_widget = QtWidgets.QLabel( - "Hide studio overrides", hide_studio_overrides_widget - ) - hide_studio_overrides_layout.addWidget(_label_widget) - hide_studio_overrides_layout.addWidget(hide_studio_overrides) - - footer_layout.addWidget(save_as_default_btn, 0) - footer_layout.addWidget(refresh_button, 0) - footer_layout.addWidget(hide_studio_overrides_widget, 0) - - save_btn = QtWidgets.QPushButton("Save") - spacer_widget = QtWidgets.QWidget() - footer_layout.addWidget(spacer_widget, 1) - footer_layout.addWidget(save_btn, 0) - - configurations_widget = QtWidgets.QWidget() - configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) - configurations_layout.setContentsMargins(0, 0, 0, 0) - configurations_layout.setSpacing(0) - - configurations_layout.addWidget(scroll_widget, 1) - configurations_layout.addWidget(footer_widget, 0) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - self.setLayout(layout) - - layout.addWidget(project_list_widget, 0) - layout.addWidget(configurations_widget, 1) - - save_btn.clicked.connect(self._save_overrides) project_list_widget.project_changed.connect(self._on_project_change) self.project_list_widget = project_list_widget - self.scroll_widget = scroll_widget - self.content_layout = content_layout - self.content_widget = content_widget - self.reset() + def defaults_dir(self): + return DEFAULTS_DIR - def any_parent_overriden(self): - return False - - @property - def ignore_value_changes(self): - return self._ignore_value_changes - - @ignore_value_changes.setter - def ignore_value_changes(self, value): - self._ignore_value_changes = value - if value is False: - self.hierarchical_style_update() - - def hierarchical_style_update(self): - for input_field in self.input_fields: - input_field.hierarchical_style_update() - - def reset(self): - self.input_fields.clear() - while self.content_layout.count() != 0: - widget = self.content_layout.itemAt(0).widget() - self.content_layout.removeWidget(widget) - widget.deleteLater() - - self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") - self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema) - self._update_values() - self.hierarchical_style_update() - - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - klass = lib.TypeToKlass.types.get(item_type) - item = klass(child_configuration, self) - item.create_ui() - self.input_fields.append(item) - self.content_layout.addWidget(item, 0) - - # Add spacer to stretch children guis - spacer = QtWidgets.QWidget(self.content_widget) - self.content_layout.addWidget(spacer, 1) + def validate_defaults_to_save(self, _): + # Projects does not have any specific validations + return True def _on_project_change(self): project_name = self.project_list_widget.project_name() @@ -668,7 +553,7 @@ class ProjectWidget(QtWidgets.QWidget): _project_anatomy = project_anatomy_overrides(project_name) self.is_overidable = True - overrides = {"project": { + overrides = {self.main_schema_key: { PROJECT_SETTINGS_KEY: lib.convert_overrides_to_gui_data( _project_overrides ), @@ -682,103 +567,19 @@ class ProjectWidget(QtWidgets.QWidget): item.apply_overrides(overrides) self.ignore_value_changes = False - def _save_as_defaults(self): - output = {} - for item in self.input_fields: - output.update(item.config_value()) - - for key in reversed(self.keys): - _output = {key: output} - output = _output - - all_values = {} - for item in self.input_fields: - all_values.update(item.config_value()) - - all_values = lib.convert_gui_data_with_metadata(all_values) - - for key in reversed(self.keys): - _all_values = {key: all_values} - all_values = _all_values - - # Skip first key - all_values = all_values["project"] - - keys_to_file = lib.file_keys_from_schema(self.schema) - for key_sequence in keys_to_file: - # Skip first key - key_sequence = key_sequence[1:] - subpath = "/".join(key_sequence) + ".json" - - new_values = all_values - for key in key_sequence: - new_values = new_values[key] - - output_path = os.path.join(DEFAULTS_DIR, subpath) - dirpath = os.path.dirname(output_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to: ", subpath) - with open(output_path, "w") as file_stream: - json.dump(new_values, file_stream, indent=4) - - reset_default_settings() - - self._update_values() - self.hierarchical_style_update() - - def items_are_valid(self): - has_invalid = False - for item in self.input_fields: - if item.child_invalid: - has_invalid = True - - if not has_invalid: - return True - - invalid_items = [] - for item in self.input_fields: - invalid_items.extend(item.get_invalid()) - msg_box = QtWidgets.QMessageBox( - QtWidgets.QMessageBox.Warning, - "Invalid input", - "There is invalid value in one of inputs." - " Please lead red color and fix them." - ) - msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) - msg_box.exec_() - - first_invalid_item = invalid_items[0] - self.scroll_widget.ensureWidgetVisible(first_invalid_item) - if first_invalid_item.isVisible(): - first_invalid_item.setFocus(True) - return False - - def _on_refresh(self): - self.reset() - - def _on_hide_studio_overrides(self, state): - self._hide_studio_overrides = (state == QtCore.Qt.Checked) - self._update_values() - self.hierarchical_style_update() - - def _save_overrides(self): - if not self.items_are_valid(): - return - + def save(self): data = {} studio_overrides = bool(self.project_name is None) for item in self.input_fields: if studio_overrides: - value, is_group = item.studio_overrides() + value, _is_group = item.studio_overrides() else: - value, is_group = item.overrides() + value, _is_group = item.overrides() if value is not lib.NOT_SET: data.update(value) output_data = lib.convert_gui_data_to_overrides( - data.get("project") or {} + data.get(self.main_schema_key) or {} ) # Saving overrides data @@ -796,11 +597,9 @@ class ProjectWidget(QtWidgets.QWidget): # Update saved values self._update_values() - def _update_values(self): - self.ignore_value_changes = True - + def update_values(self): default_values = lib.convert_data_to_gui_data( - {"project": default_settings()} + {self.main_schema_key: default_settings()} ) for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -808,12 +607,12 @@ class ProjectWidget(QtWidgets.QWidget): if self._hide_studio_overrides: studio_values = lib.NOT_SET else: - studio_values = lib.convert_overrides_to_gui_data({"project": { - PROJECT_SETTINGS_KEY: studio_project_settings(), - PROJECT_ANATOMY_KEY: studio_project_anatomy() - }}) + studio_values = lib.convert_overrides_to_gui_data({ + self.main_schema_key: { + PROJECT_SETTINGS_KEY: studio_project_settings(), + PROJECT_ANATOMY_KEY: studio_project_anatomy() + } + }) for input_field in self.input_fields: input_field.update_studio_values(studio_values) - - self.ignore_value_changes = False From f19a240f689b128895fc63b8afc84afec6ef0c29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 17:06:23 +0100 Subject: [PATCH 185/219] 0_project_gui_schema renamed to schema_main --- .../{0_project_gui_schema.json => schema_main.json} | 0 .../settings/gui_schemas/system_schema/schema_main.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename pype/tools/settings/settings/gui_schemas/projects_schema/{0_project_gui_schema.json => schema_main.json} (100%) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json index d5c7402dcf..ae6622098e 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_main.json @@ -4,7 +4,7 @@ "children": [{ "type": "schema", "name": "schema_general" - },{ + }, { "type": "schema", "name": "schema_modules" }, { From ce0465aab961ab547c6699f82fdce5afdbe56f7f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 18:28:43 +0100 Subject: [PATCH 186/219] anatomy item is loading schemas dynamically --- .../settings/widgets/anatomy_types.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 83183e7943..9a20df3ae4 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -37,9 +37,7 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): "representation": "png" } - def __init__( - self, schema_data, parent, as_widget=False - ): + def __init__(self, schema_data, parent, as_widget=False): if as_widget: raise TypeError( "`AnatomyWidget` does not allow to be used as widget." @@ -49,25 +47,16 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.initial_attributes(schema_data, parent, as_widget) + self.input_fields = [] + self.key = schema_data["key"] def create_ui(self, label_widget=None): children_data = self.schema_data["children"] - roots_input_data = {} - templates_input_data = {} - imageio_input_data = {} - for child in children_data: - if child["type"] == "anatomy_roots": - roots_input_data = child - elif child["type"] == "anatomy_templates": - templates_input_data = child - elif child["key"] == "imageio": - imageio_input_data = child - - self.root_widget = RootsWidget(roots_input_data, self) - self.templates_widget = TemplatesWidget(templates_input_data, self) - self.imageio_widget = DictWidget(imageio_input_data, self) - self.imageio_widget.create_ui() + for schema_data in children_data: + item = TypeToKlass.types[schema_data["type"]](schema_data, self) + item.create_ui() + self.input_fields.append(item) self.setAttribute(QtCore.Qt.WA_StyledBackground) From c6fe33b08a9359ec5f980a6311d394f4895b8bc8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 18:29:22 +0100 Subject: [PATCH 187/219] added required modifications to be able use dynamically loaded items --- .../settings/widgets/anatomy_types.py | 136 ++++++++---------- 1 file changed, 63 insertions(+), 73 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 9a20df3ae4..ad24d54050 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -72,19 +72,15 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) content_layout.setSpacing(5) - content_layout.addWidget(self.root_widget) - content_layout.addWidget(self.templates_widget) - content_layout.addWidget(self.imageio_widget) + for input_field in self.input_fields: + content_layout.addWidget(input_field) + input_field.value_changed.connect(self._on_value_change) body_widget.set_content_widget(content_widget) self.body_widget = body_widget self.label_widget = body_widget.label_widget - self.root_widget.value_changed.connect(self._on_value_change) - self.templates_widget.value_changed.connect(self._on_value_change) - self.imageio_widget.value_changed.connect(self._on_value_change) - def update_default_values(self, parent_values): self._state = None self._child_state = None @@ -94,9 +90,8 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): else: value = NOT_SET - self.root_widget.update_default_values(value) - self.templates_widget.update_default_values(value) - self.imageio_widget.update_default_values(value) + for input_field in self.input_fields: + input_field.update_default_values(value) def update_studio_values(self, parent_values): self._state = None @@ -107,9 +102,8 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): else: value = NOT_SET - self.root_widget.update_studio_values(value) - self.templates_widget.update_studio_values(value) - self.imageio_widget.update_studio_values(value) + for input_field in self.input_fields: + input_field.update_studio_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -120,9 +114,8 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): if parent_values is not NOT_SET: value = parent_values.get(self.key, value) - self.root_widget.apply_overrides(value) - self.templates_widget.apply_overrides(value) - self.imageio_widget.apply_overrides(value) + for input_field in self.input_fields: + input_field.apply_overrides(value) def set_value(self, value): raise TypeError("AnatomyWidget does not allow to use `set_value`") @@ -156,67 +149,58 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self._child_state = child_state def hierarchical_style_update(self): - self.root_widget.hierarchical_style_update() - self.templates_widget.hierarchical_style_update() - self.imageio_widget.hierarchical_style_update() + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() @property def child_has_studio_override(self): - return ( - self.root_widget.child_has_studio_override - or self.templates_widget.child_has_studio_override - or self.imageio_widget.child_has_studio_override - ) + for input_field in self.input_fields: + if input_field.child_has_studio_override: + return True + return False @property def child_modified(self): - return ( - self.root_widget.child_modified - or self.templates_widget.child_modified - or self.imageio_widget.child_modified - ) + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False @property def child_overriden(self): - return ( - self.root_widget.child_overriden - or self.templates_widget.child_overriden - or self.imageio_widget.child_overriden - ) + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False @property def child_invalid(self): - return ( - self.root_widget.child_invalid - or self.templates_widget.child_invalid - or self.imageio_widget.child_invalid - ) + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False def set_as_overriden(self): - self.root_widget.set_as_overriden() - self.templates_widget.set_as_overriden() - self.imageio_widget.set_as_overriden() + for input_field in self.input_fields: + input_field.child_invalid.set_as_overriden() def remove_overrides(self): - self.root_widget.remove_overrides() - self.templates_widget.remove_overrides() - self.imageio_widget.remove_overrides() + for input_field in self.input_fields: + input_field.remove_overrides() def reset_to_pype_default(self): - self.root_widget.reset_to_pype_default() - self.templates_widget.reset_to_pype_default() - self.imageio_widget.reset_to_pype_default() + for input_field in self.input_fields: + input_field.reset_to_pype_default() def set_studio_default(self): - self.root_widget.set_studio_default() - self.templates_widget.set_studio_default() - self.imageio_widget.set_studio_default() + for input_field in self.input_fields: + input_field.set_studio_default() def discard_changes(self): - self.root_widget.discard_changes() - self.templates_widget.discard_changes() - self.imageio_widget.discard_changes() + for input_field in self.input_fields: + input_field.discard_changes() def overrides(self): if self.child_overriden: @@ -225,26 +209,30 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): def item_value(self): output = {} - output.update(self.root_widget.config_value()) - output.update(self.templates_widget.config_value()) - output.update(self.imageio_widget.config_value()) + for input_field in self.input_fields: + output.update(input_field.config_value()) return output def studio_overrides(self): - if ( - self.root_widget.child_has_studio_override - or self.templates_widget.child_has_studio_override - or self.imageio_widget.child_has_studio_override - ): - groups = [ - self.root_widget.key, - self.templates_widget.key, - self.imageio_widget.key - ] - value = self.config_value() - value[self.key][METADATA_KEY] = {"groups": groups} - return value, True - return NOT_SET, False + has_overrides = False + for input_field in self.input_fields: + if input_field.child_has_studio_override: + has_overrides = True + break + + if not has_overrides: + return NOT_SET, False + + groups = [] + for input_field in self.input_fields: + groups.append(input_field.key) + + value = self.config_value() + if METADATA_KEY not in value[self.key]: + value[self.key][METADATA_KEY] = {} + value[self.key][METADATA_KEY]["groups"] = groups + + return value, True def config_value(self): return {self.key: self.item_value()} @@ -267,6 +255,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): self.studio_is_multiroot = False self.was_multiroot = NOT_SET + def create_ui(self, _label_widget=None): checkbox_widget = QtWidgets.QWidget(self) multiroot_label = QtWidgets.QLabel( "Use multiple roots", checkbox_widget @@ -650,6 +639,7 @@ class TemplatesWidget(QtWidgets.QWidget, SettingObject): self.key = input_data["key"] + def create_ui(self, label_widget=None): body_widget = ExpandingWidget("Templates", self) content_widget = QtWidgets.QWidget(body_widget) body_widget.set_content_widget(content_widget) @@ -775,5 +765,5 @@ class TemplatesWidget(QtWidgets.QWidget, SettingObject): TypeToKlass.types["anatomy"] = AnatomyWidget -TypeToKlass.types["anatomy_roots"] = AnatomyWidget -TypeToKlass.types["anatomy_templates"] = AnatomyWidget +TypeToKlass.types["anatomy_roots"] = RootsWidget +TypeToKlass.types["anatomy_templates"] = TemplatesWidget From a294b577e3516ee74f2e55a84566f8b108c33d16 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 2 Dec 2020 19:53:54 +0100 Subject: [PATCH 188/219] respect original c-space when not using maketx --- pype/plugins/maya/publish/extract_look.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/plugins/maya/publish/extract_look.py b/pype/plugins/maya/publish/extract_look.py index 6bd202093f..4a20409809 100644 --- a/pype/plugins/maya/publish/extract_look.py +++ b/pype/plugins/maya/publish/extract_look.py @@ -172,10 +172,11 @@ class ExtractLook(pype.api.Extractor): cspace = files_metadata[filepath]["color_space"] linearise = False - if cspace == "sRGB": - linearise = True - # set its file node to 'raw' as tx will be linearized - files_metadata[filepath]["color_space"] = "raw" + if do_maketx: + if cspace == "sRGB": + linearise = True + # set its file node to 'raw' as tx will be linearized + files_metadata[filepath]["color_space"] = "raw" source, mode, hash = self._process_texture( filepath, From 3efbfd52c355c106383d883df516df8d5bf051cd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 20:47:38 +0100 Subject: [PATCH 189/219] convert settings --- .../defaults/project_anatomy/dataflow.json | 55 --- .../defaults/project_settings/Ftrack.json | 25 ++ .../defaults/project_settings/attributes.json | 16 + .../ftrack/ftrack_custom_attributes.json | 171 +++---- .../ftrack/project_defaults.json | 18 - .../defaults/project_settings/global.json | 89 ++++ .../project_settings/global/creator.json | 8 - .../global/project_folder_structure.json | 22 - .../project_settings/global/sw_folders.json | 8 - .../project_settings/global/workfiles.json | 7 - .../premiere/asset_default.json | 5 - .../premiere/rules_tasks.json | 21 - .../defaults/project_settings/unreal.json | 6 + .../unreal/project_setup.json | 4 - .../system_settings/applications.json | 4 + .../defaults/system_settings/modules.json | 26 -- .../projects_schema/0_project_gui_schema.json | 4 + .../schema_project_ftrack.json | 81 ++++ .../schema_project_global.json | 419 +----------------- .../schema_project_unreal.json | 25 ++ .../schemas/schema_anatomy_attributes.json | 73 +++ .../{ => schemas}/schema_anatomy_imageio.json | 0 .../schemas/schema_global_publish.json | 399 +++++++++++++++++ .../schemas/schema_global_tools.json | 84 ++++ .../schemas/schema_maya_load.json | 1 - .../schemas/schema_maya_publish.json | 1 - .../schemas/schema_publish_gui_filter.json | 1 - .../schemas/schema_workfile_build.json | 1 - .../module_settings/schema_ftrack.json | 87 ---- 29 files changed, 891 insertions(+), 770 deletions(-) delete mode 100644 pype/settings/defaults/project_anatomy/dataflow.json create mode 100644 pype/settings/defaults/project_settings/attributes.json delete mode 100644 pype/settings/defaults/project_settings/ftrack/project_defaults.json delete mode 100644 pype/settings/defaults/project_settings/global/creator.json delete mode 100644 pype/settings/defaults/project_settings/global/project_folder_structure.json delete mode 100644 pype/settings/defaults/project_settings/global/sw_folders.json delete mode 100644 pype/settings/defaults/project_settings/global/workfiles.json delete mode 100644 pype/settings/defaults/project_settings/premiere/asset_default.json delete mode 100644 pype/settings/defaults/project_settings/premiere/rules_tasks.json create mode 100644 pype/settings/defaults/project_settings/unreal.json delete mode 100644 pype/settings/defaults/project_settings/unreal/project_setup.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_unreal.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json rename pype/tools/settings/settings/gui_schemas/projects_schema/{ => schemas}/schema_anatomy_imageio.json (100%) create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json diff --git a/pype/settings/defaults/project_anatomy/dataflow.json b/pype/settings/defaults/project_anatomy/dataflow.json deleted file mode 100644 index d2f470b5bc..0000000000 --- a/pype/settings/defaults/project_anatomy/dataflow.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "nuke": { - "nodes": { - "connected": true, - "modifymetadata": { - "_id": "connect_metadata", - "_previous": "ENDING", - "metadata.set.pype_studio_name": "{PYPE_STUDIO_NAME}", - "metadata.set.avalon_project_name": "{AVALON_PROJECT}", - "metadata.set.avalon_project_code": "{PYPE_STUDIO_CODE}", - "metadata.set.avalon_asset_name": "{AVALON_ASSET}" - }, - "crop": { - "_id": "connect_crop", - "_previous": "connect_metadata", - "box": [ - "{metadata.crop.x}", - "{metadata.crop.y}", - "{metadata.crop.right}", - "{metadata.crop.top}" - ] - }, - "write": { - "render": { - "_id": "output_write", - "_previous": "connect_crop", - "file_type": "exr", - "datatype": "16 bit half", - "compression": "Zip (1 scanline)", - "autocrop": true, - "tile_color": "0xff0000ff", - "channels": "rgb" - }, - "prerender": { - "_id": "output_write", - "_previous": "connect_crop", - "file_type": "exr", - "datatype": "16 bit half", - "compression": "Zip (1 scanline)", - "autocrop": false, - "tile_color": "0xc9892aff", - "channels": "rgba" - }, - "still": { - "_previous": "connect_crop", - "channels": "rgba", - "file_type": "tiff", - "datatype": "16 bit", - "compression": "LZW", - "tile_color": "0x4145afff" - } - } - } - } -} diff --git a/pype/settings/defaults/project_settings/Ftrack.json b/pype/settings/defaults/project_settings/Ftrack.json index 7c9bb8ce0e..023b85cb3b 100644 --- a/pype/settings/defaults/project_settings/Ftrack.json +++ b/pype/settings/defaults/project_settings/Ftrack.json @@ -2,6 +2,31 @@ "ftrack_actions_path": [], "ftrack_events_path": [], "events": { + "sync_to_avalon": { + "enabled": true, + "statuses_name_change": [ + "ready", + "not ready" + ] + }, + "push_frame_values_to_task": { + "enabled": true, + "interest_entity_types": [ + "shot", + "asset build" + ], + "interest_attributess": [ + "frameStart", + "frameEnd" + ] + }, + "thumbnail_updates": { + "enabled": true, + "levels": 2 + }, + "user_assignment": { + "enabled": true + }, "status_update": { "enabled": true, "mapping": { diff --git a/pype/settings/defaults/project_settings/attributes.json b/pype/settings/defaults/project_settings/attributes.json new file mode 100644 index 0000000000..df586c105a --- /dev/null +++ b/pype/settings/defaults/project_settings/attributes.json @@ -0,0 +1,16 @@ +{ + "fps": 25, + "frameStart": 1001, + "frameEnd": 1001, + "clipIn": 1, + "clipOut": 1, + "handleStart": 0, + "handleEnd": 0, + "resolutionWidth": 1920, + "resolutionHeight": 1080, + "pixelAspect": 1, + "applications": [ + "maya_2019", + "nuke_12.2" + ] +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json index 371be3b8d8..c4e204f716 100644 --- a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json +++ b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json @@ -1,109 +1,66 @@ -[{ - "label": "FPS", - "key": "fps", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "write_security_role": ["ALL"], - "read_security_role": ["ALL"], - "default": null, - "config": { - "isdecimal": true +{ + "show": { + "avalon_auto_sync": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "library_project": { + "default": null, + "write_security_role": [], + "read_security_role": [] + } + }, + "is_hierarchical": { + "fps": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "clipIn": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "clipOut": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "frameStart": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "frameEnd": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "resolutionWidth": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "resolutionHeight": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "pixelAspect": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "handleStart": { + "default": null, + "write_security_role": [], + "read_security_role": [] + }, + "handleEnd": { + "default": null, + "write_security_role": [], + "read_security_role": [] + } } -}, { - "label": "Avalon auto-sync", - "key": "avalon_auto_sync", - "type": "boolean", - "entity_type": "show", - "group": "avalon", - "write_security_role": ["API", "Administrator"], - "read_security_role": ["API", "Administrator"] -}, { - "label": "Intent", - "key": "intent", - "type": "enumerator", - "entity_type": "assetversion", - "group": "avalon", - "config": { - "multiselect": false, - "data": [ - {"test": "Test"}, - {"wip": "WIP"}, - {"final": "Final"} - ] - } -}, { - "label": "Library Project", - "key": "library_project", - "type": "boolean", - "entity_type": "show", - "group": "avalon", - "write_security_role": ["API", "Administrator"], - "read_security_role": ["API", "Administrator"] -}, { - "label": "Clip in", - "key": "clipIn", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Clip out", - "key": "clipOut", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Frame start", - "key": "frameStart", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Frame end", - "key": "frameEnd", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Resolution Width", - "key": "resolutionWidth", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Resolution Height", - "key": "resolutionHeight", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Pixel aspect", - "key": "pixelAspect", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "config": { - "isdecimal": true - } -}, { - "label": "Frame handles start", - "key": "handleStart", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null -}, { - "label": "Frame handles end", - "key": "handleEnd", - "type": "number", - "is_hierarchical": true, - "group": "avalon", - "default": null } -] diff --git a/pype/settings/defaults/project_settings/ftrack/project_defaults.json b/pype/settings/defaults/project_settings/ftrack/project_defaults.json deleted file mode 100644 index a4e3aa3362..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/project_defaults.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fps": 25, - "frameStart": 1001, - "frameEnd": 1100, - "clipIn": 1001, - "clipOut": 1100, - "handleStart": 10, - "handleEnd": 10, - - "resolutionHeight": 1080, - "resolutionWidth": 1920, - "pixelAspect": 1.0, - "applications": [ - "maya_2019", "nuke_11.3", "nukex_11.3", "nukestudio_11.3", "deadline" - ], - "tools_env": [], - "avalon_auto_sync": true -} diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index dd379b6180..407dd23593 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -111,5 +111,94 @@ ] } } + }, + "tools": { + "Creator": { + "families_smart_select": { + "Render": [ + "light", + "render" + ], + "Model": [ + "model" + ], + "Layout": [ + "layout" + ], + "Look": [ + "look" + ], + "Rig": [ + "rigging", + "rig" + ] + } + }, + "Workfiles": { + "last_workfile_on_startup": { + "profiles": [ + { + "hosts": [], + "tasks": [], + "enabled": true + } + ] + }, + "sw_folders": { + "compositing": [ + "nuke", + "ae" + ], + "modeling": [ + "maya", + "blender", + "zbrush" + ], + "lookdev": [ + "substance", + "textures" + ] + } + } + }, + "attributes": { + "fps": 25, + "frameStart": 1001, + "frameEnd": 1001, + "clipIn": 1, + "clipOut": 1, + "handleStart": 0, + "handleEnd": 0, + "resolutionWidth": 1920, + "resolutionHeight": 1008, + "pixelAspect": 1, + "applications": [ + "maya_2020", + "nuke_12.2", + "hiero_12.2", + "blender_2.91" + ] + }, + "project_folder_structure": { + "__project_root__": { + "prod": {}, + "resources": { + "footage": { + "plates": {}, + "offline": {} + }, + "audio": {}, + "art_dept": {} + }, + "editorial": {}, + "assets[ftrack.Library]": { + "characters[ftrack]": {}, + "locations[ftrack]": {} + }, + "shots[ftrack.Sequence]": { + "scripts": {}, + "editorial[ftrack.Folder]": {} + } + } } } \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/global/creator.json b/pype/settings/defaults/project_settings/global/creator.json deleted file mode 100644 index d14e779f01..0000000000 --- a/pype/settings/defaults/project_settings/global/creator.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Model": ["model"], - "Render Globals": ["light", "render"], - "Layout": ["layout"], - "Set Dress": ["setdress"], - "Look": ["look"], - "Rig": ["rigging"] -} diff --git a/pype/settings/defaults/project_settings/global/project_folder_structure.json b/pype/settings/defaults/project_settings/global/project_folder_structure.json deleted file mode 100644 index 83bd5f12a9..0000000000 --- a/pype/settings/defaults/project_settings/global/project_folder_structure.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "__project_root__": { - "prod" : {}, - "resources" : { - "footage": { - "plates": {}, - "offline": {} - }, - "audio": {}, - "art_dept": {} - }, - "editorial" : {}, - "assets[ftrack.Library]": { - "characters[ftrack]": {}, - "locations[ftrack]": {} - }, - "shots[ftrack.Sequence]": { - "scripts": {}, - "editorial[ftrack.Folder]": {} - } - } -} diff --git a/pype/settings/defaults/project_settings/global/sw_folders.json b/pype/settings/defaults/project_settings/global/sw_folders.json deleted file mode 100644 index a154935dce..0000000000 --- a/pype/settings/defaults/project_settings/global/sw_folders.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compositing": ["nuke", "ae"], - "modeling": ["maya", "app2"], - "lookdev": ["substance"], - "animation": [], - "lighting": [], - "rigging": [] -} diff --git a/pype/settings/defaults/project_settings/global/workfiles.json b/pype/settings/defaults/project_settings/global/workfiles.json deleted file mode 100644 index 393b2e3c10..0000000000 --- a/pype/settings/defaults/project_settings/global/workfiles.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "last_workfile_on_startup": [ - { - "enabled": false - } - ] -} diff --git a/pype/settings/defaults/project_settings/premiere/asset_default.json b/pype/settings/defaults/project_settings/premiere/asset_default.json deleted file mode 100644 index 84d2bde3d8..0000000000 --- a/pype/settings/defaults/project_settings/premiere/asset_default.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "frameStart": 1001, - "handleStart": 0, - "handleEnd": 0 -} diff --git a/pype/settings/defaults/project_settings/premiere/rules_tasks.json b/pype/settings/defaults/project_settings/premiere/rules_tasks.json deleted file mode 100644 index 333c9cd70b..0000000000 --- a/pype/settings/defaults/project_settings/premiere/rules_tasks.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "defaultTasks": ["Layout", "Animation"], - "taskToSubsets": { - "Layout": ["reference", "audio"], - "Animation": ["audio"] - }, - "subsetToRepresentations": { - "reference": { - "preset": "h264", - "representation": "mp4" - }, - "thumbnail": { - "preset": "jpeg_thumb", - "representation": "jpg" - }, - "audio": { - "preset": "48khz", - "representation": "wav" - } - } -} diff --git a/pype/settings/defaults/project_settings/unreal.json b/pype/settings/defaults/project_settings/unreal.json new file mode 100644 index 0000000000..46b9ca2a18 --- /dev/null +++ b/pype/settings/defaults/project_settings/unreal.json @@ -0,0 +1,6 @@ +{ + "project_setup": { + "dev_mode": true, + "install_unreal_python_engine": false + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/unreal/project_setup.json b/pype/settings/defaults/project_settings/unreal/project_setup.json deleted file mode 100644 index 8a4dffc526..0000000000 --- a/pype/settings/defaults/project_settings/unreal/project_setup.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "dev_mode": false, - "install_unreal_python_engine": false -} diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index b0e9635ca8..1393b94ff5 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -944,6 +944,10 @@ [ "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 (64bits)\\TVPaint Animation 11 (64bits).exe", "" + ], + [ + "C:\\Program Files\\TVPaint Developpement\\TVPaint Animation 11 Pro (64bits) (DEMO)\\TVPaint Animation 11 Pro (64bits) (DEMO).exe", + "" ] ], "darwin": [], diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 386e37b246..cf2372dece 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -27,32 +27,6 @@ "ftrack_events_path": [], "FTRACK_EVENTS_MONGO_DB": "pype", "FTRACK_EVENTS_MONGO_COL": "ftrack_events", - "events": { - "sync_to_avalon": { - "enabled": true, - "statuses_name_change": [ - "not ready", - "ready" - ] - }, - "push_frame_values_to_task": { - "enabled": true, - "interest_entity_types": [ - "shot" - ], - "interest_attributess": [ - "frameStart", - "frameEnd" - ] - }, - "thumbnail_updates": { - "enabled": true, - "levels": 2 - }, - "user_assignment": { - "enabled": true - } - }, "intent": { "items": { "-": "-", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index da949699ad..06937778d6 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -54,6 +54,10 @@ { "type": "schema", "name": "schema_project_standalonepublisher" + }, + { + "type": "schema", + "name": "schema_project_unreal" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json index a7c835081a..3b784accc1 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json @@ -33,6 +33,87 @@ "key": "events", "label": "Server Events", "children": [ + { + "type": "dict", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Allow name and hierarchy change only if following statuses are on all children tasks" + }, + { + "type": "list", + "key": "statuses_name_change", + "label": "Statuses", + "object_type": { + "type": "text", + "multiline": false + } + } + ] + }, + { + "type": "dict", + "key": "push_frame_values_to_task", + "label": "Sync Hierarchical and Entity Attributes", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "list", + "key": "interest_entity_types", + "label": "Entity types of interest", + "object_type": { + "type": "text", + "multiline": false + } + }, { + "type": "list", + "key": "interest_attributess", + "label": "Attributes to sync", + "object_type": { + "type": "text", + "multiline": false + } + }] + }, + { + "type": "dict", + "key": "thumbnail_updates", + "label": "Update Hierarchy thumbnails", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + },{ + "type": "label", + "label": "Push thumbnail from version, up through multiple hierarchy levels." + },{ + "type": "number", + "key": "levels", + "label": "Levels" + }] + }, + { + "type": "dict", + "key": "user_assignment", + "label": "Run script on user assignments", + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { "type": "dict", "key": "status_update", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json index 6fdc6a23a5..d78d430e9b 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json @@ -6,403 +6,26 @@ "is_file": true, "children": [ { - "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, + "type": "schema", + "name": "schema_global_publish" + }, + { + "type": "schema", + "name": "schema_global_tools" + }, + { + "type": "schema", + "name": "schema_anatomy_attributes" + }, + { + "type": "collapsible-wrap", + "label": "Project Folder Structure", "children": [ - { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "IntegrateMasterVersion", - "label": "IntegrateMasterVersion", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, - { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "ExtractJpegEXR", - "label": "ExtractJpegEXR", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "dict", - "key": "ffmpeg_args", - "children": [ - { - "type": "list", - "object_type": "text", - "key": "input", - "label": "FFmpeg input arguments" - }, - { - "type": "list", - "object_type": "text", - "key": "output", - "label": "FFmpeg output arguments" - }] - }] - }, - { - "type": "dict", - "collapsable": true, - "key": "ExtractReview", - "label": "ExtractReview", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "list", - "key": "profiles", - "label": "Profiles", - "object_type": - { - "type": "dict", - "children": [ - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }, - { - "key": "hosts", - "label": "Hosts", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "key": "outputs", - "label": "Output Definitions", - "type": "dict-modifiable", - "highlight_content": true, - "object_type": - { - "type": "dict", - "children": [ - { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "tags", - "label": "Tags", - "type": "enum", - "multiselection": true, - "enum_items": [ - { - "burnin": "Add burnins" - }, - { - "ftrackreview": "Add to Ftrack" - }, - { - "delete": "Delete output" - }, - { - "slate-frame": "Add slate frame" - }, - { - "no-hnadles": "Skip handle frames" - }] - }, - { - "key": "ffmpeg_args", - "label": "FFmpeg arguments", - "type": "dict", - "highlight_content": true, - "children": [ - { - "key": "video_filters", - "label": "Video filters", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "key": "audio_filters", - "label": "Audio filters", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "key": "input", - "label": "Input arguments", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "key": "output", - "label": "Output arguments", - "type": "list", - "object_type": "text" - }] - }, - { - "key": "filter", - "label": "Additional output filtering", - "type": "dict", - "highlight_content": true, - "children": [ - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }] - }] - } - }] - } - }] - }, - { - "type": "dict", - "collapsable": true, - "key": "ExtractBurnin", - "label": "ExtractBurnin", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "dict", - "collapsable": true, - "key": "options", - "label": "Burnin formating options", - "children": [ - { - "type": "number", - "key": "font_size", - "label": "Font size" - }, - { - "type": "number", - "key": "opacity", - "label": "Font opacity" - }, - { - "type": "number", - "key": "bg_opacity", - "label": "Background opacity" - }, - { - "type": "number", - "key": "x_offset", - "label": "X Offset" - }, - { - "type": "number", - "key": "y_offset", - "label": "Y Offset" - }, - { - "type": "number", - "key": "bg_padding", - "label": "Padding aroung text" - }, - { - "type": "splitter" - }] - }, - - { - "type": "list", - "key": "profiles", - "label": "Profiles", - "object_type": - { - "type": "dict", - "children": [ - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }, - { - "key": "hosts", - "label": "Hosts", - "type": "list", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "key": "burnins", - "label": "Burnins", - "type": "dict-modifiable", - "highlight_content": true, - "collapsable": false, - "object_type": - { - "type": "dict", - "children": [ - { - "key": "TOP_LEFT", - "label": "Top Left", - "type": "text" - }, - { - "key": "TOP_CENTERED", - "label": "Top Centered", - "type": "text" - }, - { - "key": "TOP_RIGHT", - "label": "top Right", - "type": "text" - }, - { - "key": "BOTTOM_LEFT", - "label": "Bottom Left", - "type": "text" - }, - { - "key": "BOTTOM_CENTERED", - "label": "Bottom Centered", - "type": "text" - }, - { - "key": "BOTTOM_RIGHT", - "label": "BottomRight", - "type": "text" - }] - } - }] - } - } - ] - }, - { - "type": "dict", - "collapsable": true, - "key": "IntegrateAssetNew", - "label": "IntegrateAssetNew", - "is_group": true, - "children": [ - { - "type": "raw-json", - "key": "template_name_profiles", - "label": "template_name_profiles" - }] - }, - { - "type": "dict", - "collapsable": true, - "key": "ProcessSubmittedJobOnFarm", - "label": "ProcessSubmittedJobOnFarm", - "checkbox_key": "enabled", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "deadline_department", - "label": "Deadline department" - }, - { - "type": "text", - "key": "deadline_pool", - "label": "Deadline Pool" - }, - { - "type": "text", - "key": "deadline_group", - "label": "Deadline Group" - }, - { - "type": "text", - "key": "deadline_chunk_size", - "label": "Deadline Chunk Size" - }, - { - "type": "text", - "key": "deadline_priority", - "label": "Deadline Priotity" - }, - { - "type": "dict", - "key": "aov_filter", - "label": "Reviewable subsets filter", - "children": [ - { - "type": "list", - "key": "maya", - "label": "Maya", - "object_type": - { - "type": "text" - } - }, - { - "type": "list", - "key": "nuke", - "label": "Nuke", - "object_type": - { - "type": "text" - } - }, - { - "type": "list", - "key": "aftereffects", - "label": "After Effects", - "object_type": - { - "type": "text" - } - }, - { - "type": "list", - "key": "celaction", - "label": "Celaction", - "object_type": - { - "type": "text" - } - }] - }] - }] - }] + { + "type": "raw-json", + "key": "project_folder_structure", + "label": "" + }] + } + ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_unreal.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_unreal.json new file mode 100644 index 0000000000..63cfdec639 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_unreal.json @@ -0,0 +1,25 @@ +{ + "type": "dict", + "collapsable": true, + "key": "unreal", + "label": "Unreal Engine", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "project_setup", + "label": "Project Setup", + "children": [ + { + "type": "boolean", + "key": "dev_mode", + "label": "Dev mode" + }, + { + "type": "boolean", + "key": "install_unreal_python_engine", + "label": "Install unreal python engine" + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json new file mode 100644 index 0000000000..c56e4432e3 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -0,0 +1,73 @@ +{ + "type": "dict", + "collapsable": true, + "key": "attributes", + "label": "Project Attribute Defaults", + "is_file": true, + "children": [ + { + "type": "number", + "key": "fps", + "label": "Frame Rate" + }, + { + "type": "number", + "key": "frameStart", + "label": "Frame Start" + }, + { + "type": "number", + "key": "frameEnd", + "label": "Frame End" + }, + { + "type": "number", + "key": "clipIn", + "label": "Clip In" + }, + { + "type": "number", + "key": "clipOut", + "label": "Clip Out" + }, + { + "type": "number", + "key": "handleStart", + "label": "Handle Start" + }, + { + "type": "number", + "key": "handleEnd", + "label": "Handle End" + }, + { + "type": "number", + "key": "resolutionWidth", + "label": "Resolution Width" + }, + { + "type": "number", + "key": "resolutionHeight", + "label": "Resolution Height" + }, + { + "type": "number", + "key": "pixelAspect", + "label": "Pixel Aspect Ratio" + }, + { + "type": "enum", + "key": "applications", + "label": "Applications", + "multiselection": true, + "enum_items": [ + {"maya_2020" : "Maya 2020"}, + {"nuke_12.2": "Nuke 12.2"}, + {"hiero_12.2": "Hiero 12.2"}, + {"houdini_18": "Houdini 18"}, + {"blender_2.91": "Blender 2.91"}, + {"aftereffects_2021": "After Effects 2021"} + ] + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_imageio.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/projects_schema/schema_anatomy_imageio.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_imageio.json diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json new file mode 100644 index 0000000000..86c6f2963e --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json @@ -0,0 +1,399 @@ +{ + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "IntegrateMasterVersion", + "label": "IntegrateMasterVersion", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ExtractJpegEXR", + "label": "ExtractJpegEXR", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict", + "key": "ffmpeg_args", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "input", + "label": "FFmpeg input arguments" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "FFmpeg output arguments" + }] + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractReview", + "label": "ExtractReview", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": + { + "type": "dict", + "children": [ + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "tags", + "label": "Tags", + "type": "enum", + "multiselection": true, + "enum_items": [ + { + "burnin": "Add burnins" + }, + { + "ftrackreview": "Add to Ftrack" + }, + { + "delete": "Delete output" + }, + { + "slate-frame": "Add slate frame" + }, + { + "no-hnadles": "Skip handle frames" + }] + }, + { + "key": "ffmpeg_args", + "label": "FFmpeg arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "video_filters", + "label": "Video filters", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "audio_filters", + "label": "Audio filters", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "input", + "label": "Input arguments", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "output", + "label": "Output arguments", + "type": "list", + "object_type": "text" + }] + }, + { + "key": "filter", + "label": "Additional output filtering", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }] + }] + } + }] + } + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractBurnin", + "label": "ExtractBurnin", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict", + "collapsable": true, + "key": "options", + "label": "Burnin formating options", + "children": [ + { + "type": "number", + "key": "font_size", + "label": "Font size" + }, + { + "type": "number", + "key": "opacity", + "label": "Font opacity" + }, + { + "type": "number", + "key": "bg_opacity", + "label": "Background opacity" + }, + { + "type": "number", + "key": "x_offset", + "label": "X Offset" + }, + { + "type": "number", + "key": "y_offset", + "label": "Y Offset" + }, + { + "type": "number", + "key": "bg_padding", + "label": "Padding aroung text" + }, + { + "type": "splitter" + }] + }, + + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "burnins", + "label": "Burnins", + "type": "dict-modifiable", + "highlight_content": true, + "collapsable": false, + "object_type": + { + "type": "dict", + "children": [ + { + "key": "TOP_LEFT", + "label": "Top Left", + "type": "text" + }, + { + "key": "TOP_CENTERED", + "label": "Top Centered", + "type": "text" + }, + { + "key": "TOP_RIGHT", + "label": "top Right", + "type": "text" + }, + { + "key": "BOTTOM_LEFT", + "label": "Bottom Left", + "type": "text" + }, + { + "key": "BOTTOM_CENTERED", + "label": "Bottom Centered", + "type": "text" + }, + { + "key": "BOTTOM_RIGHT", + "label": "BottomRight", + "type": "text" + }] + } + }] + } + } + ] + }, + { + "type": "dict", + "collapsable": true, + "key": "IntegrateAssetNew", + "label": "IntegrateAssetNew", + "is_group": true, + "children": [ + { + "type": "raw-json", + "key": "template_name_profiles", + "label": "template_name_profiles" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "ProcessSubmittedJobOnFarm", + "label": "ProcessSubmittedJobOnFarm", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "deadline_department", + "label": "Deadline department" + }, + { + "type": "text", + "key": "deadline_pool", + "label": "Deadline Pool" + }, + { + "type": "text", + "key": "deadline_group", + "label": "Deadline Group" + }, + { + "type": "text", + "key": "deadline_chunk_size", + "label": "Deadline Chunk Size" + }, + { + "type": "text", + "key": "deadline_priority", + "label": "Deadline Priotity" + }, + { + "type": "dict", + "key": "aov_filter", + "label": "Reviewable subsets filter", + "children": [ + { + "type": "list", + "key": "maya", + "label": "Maya", + "object_type": + { + "type": "text" + } + }, + { + "type": "list", + "key": "nuke", + "label": "Nuke", + "object_type": + { + "type": "text" + } + }, + { + "type": "list", + "key": "aftereffects", + "label": "After Effects", + "object_type": + { + "type": "text" + } + }, + { + "type": "list", + "key": "celaction", + "label": "Celaction", + "object_type": + { + "type": "text" + } + }] + }] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json new file mode 100644 index 0000000000..5a19e4827d --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json @@ -0,0 +1,84 @@ +{ + "type": "dict", + "collapsable": true, + "key": "tools", + "label": "Tools", + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "Creator", + "label": "Creator", + "children": [ + { + "type": "dict-modifiable", + "collapsable": false, + "key": "families_smart_select", + "label": "Families smart select", + "object_type":{ + "type": "list", + "object_type": "text" + } + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "Workfiles", + "label": "Workfiles", + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "last_workfile_on_startup", + "label": "Open last workfiles on launch", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, + { + "key": "tasks", + "label": "Tasks", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + } + } + ] + }, + { + "type": "dict-modifiable", + "collapsable": true, + "key": "sw_folders", + "label": "Extra task folders", + "is_group": true, + "object_type":{ + "type": "list", + "object_type": "text" + } + } + ] + }] +} diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json index 0b96e63124..28750eb795 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_load.json @@ -3,7 +3,6 @@ "collapsable": true, "key": "load", "label": "Loader plugins", - "is_file": true, "children": [ { "type": "dict", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json index 44972cad21..33d0a06d2c 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json @@ -3,7 +3,6 @@ "collapsable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "label", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json index f2385996eb..2acb5730a3 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_publish_gui_filter.json @@ -3,7 +3,6 @@ "collapsable": true, "key": "filters", "label": "Publish GUI Filters", - "is_file": true, "object_type": { "type": "raw-json", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json index d8d5b0b09b..054f009ad4 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_workfile_build.json @@ -3,7 +3,6 @@ "collapsable": true, "key": "workfile_build", "label": "Workfile Build Settings", - "is_group": true, "children": [{ "type": "list", "key": "profiles", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 8dd219e98e..92fddc6298 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -50,93 +50,6 @@ "key": "FTRACK_EVENTS_MONGO_COL", "label": "Events Mongo Collection" }, - { - "type": "dict", - "key": "events", - "label": "Server Events", - "children": [{ - "type": "dict", - "key": "sync_to_avalon", - "label": "Sync to avalon", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Allow name and hierarchy change only if following statuses are on all children tasks" - }, - { - "type": "list", - "key": "statuses_name_change", - "label": "Statuses", - "object_type": { - "type": "text", - "multiline": false - } - } - ] - }, - { - "type": "dict", - "key": "push_frame_values_to_task", - "label": "Sync Hierarchical and Entity Attributes", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "key": "interest_entity_types", - "label": "Entity types of interest", - "object_type": { - "type": "text", - "multiline": false - } - }, { - "type": "list", - "key": "interest_attributess", - "label": "Attributes to sync", - "object_type": { - "type": "text", - "multiline": false - } - }] - }, - { - "type": "dict", - "key": "thumbnail_updates", - "label": "Update Hierarchy thumbnails", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - },{ - "type": "label", - "label": "Push thumbnail from version, up through multiple hierarchy levels." - },{ - "type": "number", - "key": "levels", - "label": "Levels" - }] - }, - { - "type": "dict", - "key": "user_assignment", - "label": "Run script on user assignments", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - } - ] - }, { "type": "splitter" }, From 768be9645a7cad7efdba49c070e67ddb95698dc7 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 20:56:06 +0100 Subject: [PATCH 190/219] move attributes to anatomy --- .../defaults/environments/avalon.json | 16 ---------- .../defaults/environments/global.json | 32 ------------------- .../defaults/project_anatomy/attributes.json | 13 ++++++++ .../defaults/project_settings/global.json | 18 ----------- .../projects_schema/0_project_gui_schema.json | 7 +++- .../schema_project_global.json | 5 +-- .../schemas/schema_anatomy_attributes.json | 2 +- 7 files changed, 21 insertions(+), 72 deletions(-) delete mode 100644 pype/settings/defaults/environments/avalon.json delete mode 100644 pype/settings/defaults/environments/global.json create mode 100644 pype/settings/defaults/project_anatomy/attributes.json diff --git a/pype/settings/defaults/environments/avalon.json b/pype/settings/defaults/environments/avalon.json deleted file mode 100644 index 832ba07e71..0000000000 --- a/pype/settings/defaults/environments/avalon.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "AVALON_CONFIG": "pype", - "AVALON_PROJECTS": "{PYPE_PROJECTS_PATH}", - "AVALON_USERNAME": "avalon", - "AVALON_PASSWORD": "secret", - "AVALON_DEBUG": "1", - "AVALON_MONGO": "mongodb://localhost:2707", - "AVALON_DB": "avalon", - "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", - "AVALON_EARLY_ADOPTER": "1", - "AVALON_SCHEMA": "{PYPE_MODULE_ROOT}/schema", - "AVALON_LOCATION": "http://127.0.0.1", - "AVALON_LABEL": "Pype", - "AVALON_TIMEOUT": "1000", - "AVALON_THUMBNAIL_ROOT": "" -} diff --git a/pype/settings/defaults/environments/global.json b/pype/settings/defaults/environments/global.json deleted file mode 100644 index 717e337db8..0000000000 --- a/pype/settings/defaults/environments/global.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "PYPE_STUDIO_NAME": "Studio Name", - "PYPE_STUDIO_CODE": "stu", - "PYPE_APP_ROOT": "{PYPE_SETUP_PATH}/pypeapp", - "PYPE_MODULE_ROOT": "{PYPE_SETUP_PATH}/repos/pype", - "PYPE_PROJECT_PLUGINS": "", - "STUDIO_SOFT": "{PYP_SETUP_ROOT}/soft", - "FFMPEG_PATH": { - "windows": "{VIRTUAL_ENV}/localized/ffmpeg_exec/windows/bin;{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/windows/bin", - "darwin": "{VIRTUAL_ENV}/localized/ffmpeg_exec/darwin/bin:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/darwin/bin", - "linux": "{VIRTUAL_ENV}/localized/ffmpeg_exec/linux:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/linux" - }, - "PATH": [ - "{PYPE_CONFIG}/launchers", - "{PYPE_APP_ROOT}", - "{FFMPEG_PATH}", - "{PATH}" - ], - "PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs", - "PYTHONPATH": { - "windows": "{VIRTUAL_ENV}/Lib/site-packages;{PYPE_MODULE_ROOT}/pype/tools;{PYTHONPATH}", - "linux": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}", - "darwin": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}" - }, - "PYPE_PROJECT_CONFIGS": "{PYPE_SETUP_PATH}/../studio-project-configs", - "PYPE_PYTHON_EXE": { - "windows": "{VIRTUAL_ENV}/Scripts/python.exe", - "linux": "{VIRTUAL_ENV}/Scripts/python", - "darwin": "{VIRTUAL_ENV}/bin/python" - }, - "PYBLISH_GUI": "pyblish_pype" -} diff --git a/pype/settings/defaults/project_anatomy/attributes.json b/pype/settings/defaults/project_anatomy/attributes.json new file mode 100644 index 0000000000..fbf0218999 --- /dev/null +++ b/pype/settings/defaults/project_anatomy/attributes.json @@ -0,0 +1,13 @@ +{ + "fps": 25, + "frameStart": 1001, + "frameEnd": 1001, + "clipIn": 1, + "clipOut": 1, + "handleStart": 0, + "handleEnd": 0, + "resolutionWidth": 1920, + "resolutionHeight": 1080, + "pixelAspect": 1, + "applications": [] +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index 407dd23593..7cedd7cf88 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -161,24 +161,6 @@ } } }, - "attributes": { - "fps": 25, - "frameStart": 1001, - "frameEnd": 1001, - "clipIn": 1, - "clipOut": 1, - "handleStart": 0, - "handleEnd": 0, - "resolutionWidth": 1920, - "resolutionHeight": 1008, - "pixelAspect": 1, - "applications": [ - "maya_2020", - "nuke_12.2", - "hiero_12.2", - "blender_2.91" - ] - }, "project_folder_structure": { "__project_root__": { "prod": {}, diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index 06937778d6..837eab0b43 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -14,7 +14,12 @@ "type": "anatomy_templates", "key": "templates", "is_file": true - }, { + }, + { + "type": "schema", + "name": "schema_anatomy_attributes" + }, + { "type": "schema", "name": "schema_anatomy_imageio" } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json index d78d430e9b..75731fe207 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_global.json @@ -13,10 +13,7 @@ "type": "schema", "name": "schema_global_tools" }, - { - "type": "schema", - "name": "schema_anatomy_attributes" - }, + { "type": "collapsible-wrap", "label": "Project Folder Structure", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json index c56e4432e3..6283100fca 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -2,7 +2,7 @@ "type": "dict", "collapsable": true, "key": "attributes", - "label": "Project Attribute Defaults", + "label": "Attribute Defaults", "is_file": true, "children": [ { From 2960e994f213d2ef061a260889175b942189167d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 21:08:50 +0100 Subject: [PATCH 191/219] fix argument --- pype/tools/settings/settings/widgets/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index c1ff640837..9e8e6537f1 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -357,7 +357,7 @@ class SystemWidget(SettingsCategoryWidget): self.environ_fields.clear() super(SystemWidget, self).reset() - def save(self, all_values): + def save(self): _data = {} for input_field in self.input_fields: value, _is_group = input_field.studio_overrides() From 3449779fdea8b392c9d278b5fd720200c53c8515 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:10:45 +0100 Subject: [PATCH 192/219] AE - added background loader Reworked structure of json metadata --- .../stubs/aftereffects_server_stub.py | 239 ++++++++++++++---- .../aftereffects/load/load_background.py | 96 +++++++ pype/plugins/aftereffects/load/load_file.py | 15 +- pype/plugins/lib.py | 43 +++- 4 files changed, 325 insertions(+), 68 deletions(-) create mode 100644 pype/plugins/aftereffects/load/load_background.py diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index 0b8c54e884..d4c1cf4512 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -4,13 +4,27 @@ from pype.modules.websocket_server import WebSocketServer Used anywhere solution is calling client methods. """ import json -from collections import namedtuple - +import attr import logging log = logging.getLogger(__name__) +@attr.s +class AEItem(object): + """ + Object denoting Item in AE. Each item is created in AE by any Loader, + but contains same fields, which are being used in later processing. + """ + # metadata + id = attr.ib() # id created by AE, could be used for querying + name = attr.ib() # name of item + item_type = attr.ib(default=None) # item type (footage, folder, comp) + # all imported elements, single for + # regular image, array for Backgrounds + members = attr.ib(factory=list) + + class AfterEffectsServerStub(): """ Stub for calling function on client (Photoshop js) side. @@ -34,22 +48,14 @@ class AfterEffectsServerStub(): ('AfterEffects.open', path=path) ) - def read(self, layer, layers_meta=None): - """ - Parses layer metadata from Label field of active document - Args: - layer: - res(string): - json representation + Returns: + res(string): - json representation """ if not res: return [] @@ -358,9 +480,14 @@ class AfterEffectsServerStub(): return [] ret = [] - # convert to namedtuple to use dot donation - if isinstance(layers_data, dict): # TODO refactore + # convert to AEItem to use dot donation + if isinstance(layers_data, dict): layers_data = [layers_data] for d in layers_data: - ret.append(namedtuple('Layer', d.keys())(*d.values())) + # currently implemented and expected fields + item = AEItem(d.get('id'), + d.get('name'), + d.get('type'), + d.get('members')) + ret.append(item) return ret diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py new file mode 100644 index 0000000000..9e7162761f --- /dev/null +++ b/pype/plugins/aftereffects/load/load_background.py @@ -0,0 +1,96 @@ +import re + +from avalon import api, aftereffects + +from pype.plugins.lib import get_background_layers, get_unique_layer_name + +stub = aftereffects.stub() + + +class BackgroundLoader(api.Loader): + """ + Load images from Background family + Creates for each background separate folder with all imported images + from background json AND automatically created composition with layers, + each layer for separate image. + + For each load container is created and stored in project (.aep) + metadata + """ + families = ["background"] + representations = ["json"] + + def load(self, context, name=None, namespace=None, data=None): + items = stub.get_items(comps=True) + existing_items = [layer.name for layer in items] + + comp_name = get_unique_layer_name(existing_items, + "{}_{}".format(context["asset"]["name"], name)) + + layers = get_background_layers(self.fname) + comp = stub.import_background(None, comp_name, layers) + + if not comp: + self.log.warning( + "Import background failed.") + self.log.warning("Check host app for alert error.") + return + + self[:] = [comp] + namespace = namespace or comp_name + + return aftereffects.containerise( + name, + namespace, + comp, + context, + self.__class__.__name__ + ) + + def update(self, container, representation): + """ Switch asset or change version """ + context = representation.get("context", {}) + + # without iterator number (_001, 002...) + namespace_from_container = re.sub(r'_\d{3}$', '', + container["namespace"]) + comp_name = "{}_{}".format(context["asset"], context["subset"]) + + # switching assets + if namespace_from_container != comp_name: + items = stub.get_items(comps=True) + existing_items = [layer.name for layer in items] + comp_name = get_unique_layer_name(existing_items, + "{}_{}".format(context["asset"], context["subset"])) + else: # switching version - keep same name + comp_name = container["namespace"] + + path = api.get_representation_path(representation) + + layers = get_background_layers(path) + comp = stub.reload_background(container["members"][1], + comp_name, + layers) + + # update container + container["representation"] = str(representation["_id"]) + container["name"] = context["subset"] + container["namespace"] = comp_name + container["members"] = comp.members + + stub.imprint(comp, container) + + def remove(self, container): + """ + Removes element from scene: deletes layer + removes from file + metadata. + Args: + container (dict): container to be removed - used to get layer_id + """ + print("!!!! container:: {}".format(container)) + layer = container.pop("layer") + stub.imprint(layer, {}) + stub.delete_item(layer.id) + + def switch(self, container, representation): + self.update(container, representation) \ No newline at end of file diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 0705dbd776..551c8f4de7 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -21,9 +21,10 @@ class FileLoader(api.Loader): representations = ["*"] def load(self, context, name=None, namespace=None, data=None): - comp_name = lib.get_unique_layer_name(stub.get_items(comps=True), - context["asset"]["name"], - name) + layers = stub.get_items(comps=True, folders=True, footages=True) + existing_layers = [layer.name for layer in layers] + comp_name = lib.get_unique_layer_name(existing_layers, + "{}_{}".format(context["asset"]["name"], name)) import_options = {} @@ -77,9 +78,11 @@ class FileLoader(api.Loader): layer_name = "{}_{}".format(context["asset"], context["subset"]) # switching assets if namespace_from_container != layer_name: - layer_name = lib.get_unique_layer_name(stub.get_items(comps=True), - context["asset"], - context["subset"]) + layers = stub.get_items(comps=True) + existing_layers = [layer.name for layer in layers] + layer_name = lib.get_unique_layer_name(existing_layers, + "{}_{}".format(context["asset"], + context["subset"])) else: # switching version - keep same name layer_name = container["namespace"] path = api.get_representation_path(representation) diff --git a/pype/plugins/lib.py b/pype/plugins/lib.py index d7d90af165..c0a4934439 100644 --- a/pype/plugins/lib.py +++ b/pype/plugins/lib.py @@ -1,22 +1,22 @@ import re +import json +import os -def get_unique_layer_name(layers, asset_name, subset_name): +def get_unique_layer_name(layers, name): """ Gets all layer names and if 'name' is present in them, increases suffix by 1 (eg. creates unique layer name - for Loader) Args: - layers (list): of namedtuples, expects 'name' field present - asset_name (string): in format asset_subset (Hero) - subset_name (string): (LOD) + layers (list): of strings, names only + name (string): checked value Returns: (string): name_00X (without version) """ - name = "{}_{}".format(asset_name, subset_name) names = {} for layer in layers: - layer_name = re.sub(r'_\d{3}$', '', layer.name) + layer_name = re.sub(r'_\d{3}$', '', layer) if layer_name in names.keys(): names[layer_name] = names[layer_name] + 1 else: @@ -24,3 +24,34 @@ def get_unique_layer_name(layers, asset_name, subset_name): occurrences = names.get(name, 0) return "{}_{:0>3d}".format(name, occurrences + 1) + + +def get_background_layers(file_url): + """ + Pulls file name from background json file, enrich with folder url for + AE to be able import files. + + Order is important, follows order in json. + + Args: + file_url (str): abs url of background json + + Returns: + (list): of abs paths to images + """ + with open(file_url) as json_file: + data = json.load(json_file) + + layers = list() + bg_folder = os.path.dirname(file_url) + for child in data['children']: + if child.get("filename"): + layers.append(os.path.join(bg_folder, child.get("filename")). + replace("\\", "/")) + else: + for layer in child['children']: + if layer.get("filename"): + layers.append(os.path.join(bg_folder, + layer.get("filename")). + replace("\\", "/")) + return layers From 96c567c9de8144c143b2b5ae7ef91ca8e813c74a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:11:28 +0100 Subject: [PATCH 193/219] AE - fix throw exc when no Render Queue --- pype/plugins/aftereffects/publish/collect_render.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index fbe392d52b..2dd447d03e 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -110,7 +110,10 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): # pull file name from Render Queue Output module render_q = aftereffects.stub().get_render_info() + if not render_q: + raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) + base_dir = self._get_output_dir(render_instance) expected_files = [] if "#" not in render_q.file_name: # single frame (mov)W From e92c842c8af4439d46ac211781aa876fa553c8c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:13:03 +0100 Subject: [PATCH 194/219] Fix - same frameStart and frameEnd didnt work --- pype/lib/abstract_collect_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/abstract_collect_render.py b/pype/lib/abstract_collect_render.py index 6bcef1ba90..19e7c37dba 100644 --- a/pype/lib/abstract_collect_render.py +++ b/pype/lib/abstract_collect_render.py @@ -73,14 +73,14 @@ class RenderInstance(object): @frameStart.validator def check_frame_start(self, _, value): """Validate if frame start is not larger then end.""" - if value >= self.frameEnd: + if value > self.frameEnd: raise ValueError("frameStart must be smaller " "or equal then frameEnd") @frameEnd.validator def check_frame_end(self, _, value): """Validate if frame end is not less then start.""" - if value <= self.frameStart: + if value < self.frameStart: raise ValueError("frameEnd must be smaller " "or equal then frameStart") From 0de8a1ddb3e05480465c4dff5dfd5fcb36636e8a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 21:18:02 +0100 Subject: [PATCH 195/219] remove old default --- .../defaults/project_settings/attributes.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 pype/settings/defaults/project_settings/attributes.json diff --git a/pype/settings/defaults/project_settings/attributes.json b/pype/settings/defaults/project_settings/attributes.json deleted file mode 100644 index df586c105a..0000000000 --- a/pype/settings/defaults/project_settings/attributes.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "fps": 25, - "frameStart": 1001, - "frameEnd": 1001, - "clipIn": 1, - "clipOut": 1, - "handleStart": 0, - "handleEnd": 0, - "resolutionWidth": 1920, - "resolutionHeight": 1080, - "pixelAspect": 1, - "applications": [ - "maya_2019", - "nuke_12.2" - ] -} \ No newline at end of file From 19be39858742df181b60ac3cfdb9c882239998eb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:34:15 +0100 Subject: [PATCH 196/219] Hound --- pype/plugins/aftereffects/load/load_background.py | 8 +++++--- pype/plugins/aftereffects/load/load_file.py | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index 9e7162761f..14f75c2850 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -24,7 +24,8 @@ class BackgroundLoader(api.Loader): items = stub.get_items(comps=True) existing_items = [layer.name for layer in items] - comp_name = get_unique_layer_name(existing_items, + comp_name = get_unique_layer_name( + existing_items, "{}_{}".format(context["asset"]["name"], name)) layers = get_background_layers(self.fname) @@ -60,7 +61,8 @@ class BackgroundLoader(api.Loader): if namespace_from_container != comp_name: items = stub.get_items(comps=True) existing_items = [layer.name for layer in items] - comp_name = get_unique_layer_name(existing_items, + comp_name = get_unique_layer_name( + existing_items, "{}_{}".format(context["asset"], context["subset"])) else: # switching version - keep same name comp_name = container["namespace"] @@ -93,4 +95,4 @@ class BackgroundLoader(api.Loader): stub.delete_item(layer.id) def switch(self, container, representation): - self.update(container, representation) \ No newline at end of file + self.update(container, representation) diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 551c8f4de7..2e07e933f7 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -23,8 +23,8 @@ class FileLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): layers = stub.get_items(comps=True, folders=True, footages=True) existing_layers = [layer.name for layer in layers] - comp_name = lib.get_unique_layer_name(existing_layers, - "{}_{}".format(context["asset"]["name"], name)) + comp_name = lib.get_unique_layer_name( + existing_layers, "{}_{}".format(context["asset"]["name"], name)) import_options = {} @@ -80,9 +80,9 @@ class FileLoader(api.Loader): if namespace_from_container != layer_name: layers = stub.get_items(comps=True) existing_layers = [layer.name for layer in layers] - layer_name = lib.get_unique_layer_name(existing_layers, - "{}_{}".format(context["asset"], - context["subset"])) + layer_name = lib.get_unique_layer_name( + existing_layers, + "{}_{}".format(context["asset"], context["subset"])) else: # switching version - keep same name layer_name = container["namespace"] path = api.get_representation_path(representation) From 722238d28329c7cef7ec850729afa0532351646a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 22:01:37 +0100 Subject: [PATCH 197/219] add ftrack custom attributes --- .../defaults/system_settings/modules.json | 66 ++++++ .../module_settings/schema_ftrack.json | 212 ++++++++++++------ 2 files changed, 209 insertions(+), 69 deletions(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index cf2372dece..15b48a636e 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -36,6 +36,72 @@ }, "default": "-" }, + "custom_attributes": { + "show": { + "avalon_auto_sync": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "library_project": { + "default": "", + "write_security_role": [], + "read_security_role": [] + } + }, + "is_hierarchical": { + "fps": { + "default": "25", + "write_security_role": [], + "read_security_role": [] + }, + "frameStart": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "frameEnd": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "clipIn": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "clipOut": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "handleStart": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "handleEnd": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "resolutionWidth": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "resolutionHeight": { + "default": "", + "write_security_role": [], + "read_security_role": [] + }, + "pixelAspect": { + "default": "", + "write_security_role": [], + "read_security_role": [] + } + } + }, "environment": { "__environment_keys__": { "ftrack": [ diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 92fddc6298..fa4cd3eea8 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -4,86 +4,160 @@ "label": "Ftrack", "collapsable": true, "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "ftrack_server", + "label": "Server" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Additional Ftrack paths" + }, + { + "type": "list", + "key": "ftrack_actions_path", + "label": "Action paths", + "object_type": "text" + }, + { + "type": "list", + "key": "ftrack_events_path", + "label": "Event paths", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Ftrack event server advanced settings" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_DB", + "label": "Event Mongo DB" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_COL", + "label": "Events Mongo Collection" + }, + { + "type": "splitter" + }, + { + "key": "intent", + "type": "dict", + "label": "Intent", + "collapsable_key": true, + "children": [ + { + "type": "label", + "label": "Intent" }, { - "type": "text", - "key": "ftrack_server", - "label": "Server" - }, - { - "type": "splitter" + "type": "dict-modifiable", + "object_type": "text", + "key": "items" }, { "type": "label", - "label": "Additional Ftrack paths" - }, - { - "type": "list", - "key": "ftrack_actions_path", - "label": "Action paths", - "object_type": "text" - }, - { - "type": "list", - "key": "ftrack_events_path", - "label": "Event paths", - "object_type": "text" - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Ftrack event server advanced settings" + "label": " " }, { + "key": "default", "type": "text", - "key": "FTRACK_EVENTS_MONGO_DB", - "label": "Event Mongo DB" - }, + "label": "Default Intent" + }] + }, + { + "key": "custom_attributes", + "label": "Custom Attributes", + "type": "dict", + "children": [ { - "type": "text", - "key": "FTRACK_EVENTS_MONGO_COL", - "label": "Events Mongo Collection" - }, - { - "type": "splitter" - }, - { - "key": "intent", - "type": "dict", - "children": [{ - "type": "label", - "label": "Intent" - }, - { - "type": "dict-modifiable", - "object_type": "text", - "key": "items" - }, - { - "type": "label", - "label": " " - }, + "type": "dict-modifiable", + "label": "Show Attributes", + "key": "show", + "object_type": + { + "type": "dict", + "children": [ { "key": "default", - "type": "text", - "label": "Default Intent" - } - ] + "label": "default", + "type": "text" + }, + { + "key": "write_security_role", + "label": "write", + "type": "list", + "object_type": + { + "type": "text" + } + }, + { + "key": "read_security_role", + "label": "Read", + "type": "list", + "object_type": + { + "type": "text" + } + }] + } }, { - "type": "splitter" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "ftrack" - } - ] + "type": "dict-modifiable", + "label": "Hierarchical Attributes", + "key": "is_hierarchical", + "object_type": + { + "type": "dict", + "children": [ + { + "key": "default", + "label": "default", + "type": "text" + }, + { + "key": "write_security_role", + "label": "write", + "type": "list", + "object_type": + { + "type": "text" + } + }, + { + "key": "read_security_role", + "label": "Read", + "type": "list", + "object_type": + { + "type": "text" + } + }] + } + }] + }, + { + "type": "splitter" + }, + { + "key": "environment", + "label": "Environment", + "type": "raw-json", + "env_group_key": "ftrack" + }] } From edc9cc3af19f5b204379ddd9f5a8e5557b62d28a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 22:02:56 +0100 Subject: [PATCH 198/219] remove old ftrack attributes --- .../ftrack/ftrack_custom_attributes.json | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json diff --git a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json deleted file mode 100644 index c4e204f716..0000000000 --- a/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "show": { - "avalon_auto_sync": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "library_project": { - "default": null, - "write_security_role": [], - "read_security_role": [] - } - }, - "is_hierarchical": { - "fps": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "clipIn": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "clipOut": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "frameStart": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "frameEnd": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "resolutionWidth": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "resolutionHeight": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "pixelAspect": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "handleStart": { - "default": null, - "write_security_role": [], - "read_security_role": [] - }, - "handleEnd": { - "default": null, - "write_security_role": [], - "read_security_role": [] - } - } -} From 81c4f21e1d9362ceab808a177be31c1a72a200cc Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 22:14:54 +0100 Subject: [PATCH 199/219] add nuke 12.2 --- .../system_settings/applications.json | 104 ++++++++++++++++++ .../defaults/system_settings/modules.json | 22 +++- .../host_settings/template_nuke.json | 5 + 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index 1393b94ff5..208a8dc5e5 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -250,6 +250,32 @@ } }, "variants": { + "nuke_12.2": { + "enabled": true, + "label": "", + "variant_label": "12.2", + "icon": "", + "executables": { + "windows": [ + [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe", + "" + ] + ], + "darwin": [], + "linux": [ + [ + "/usr/local/Nuke12.2v3Nuke12.2", + "" + ] + ] + }, + "environment": { + "__environment_keys__": { + "nuke_12.2": [] + } + } + }, "nuke_12.0": { "enabled": true, "label": "", @@ -347,6 +373,32 @@ } }, "variants": { + "nukex_12.2": { + "enabled": true, + "label": "", + "variant_label": "12.2", + "icon": "", + "executables": { + "windows": [ + [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe", + "--nukex" + ] + ], + "darwin": [], + "linux": [ + [ + "/usr/local/Nuke12.2v3Nuke12.2", + "--nukex" + ] + ] + }, + "environment": { + "__environment_keys__": { + "nukex_12.2": [] + } + } + }, "nukex_12.0": { "enabled": true, "label": "", @@ -448,6 +500,32 @@ "PYPE_LOG_NO_COLORS": "True" }, "variants": { + "nukestudio_12.2": { + "enabled": true, + "label": "", + "variant_label": "12.2", + "icon": "", + "executables": { + "windows": [ + [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe", + "--studio" + ] + ], + "darwin": [], + "linux": [ + [ + "/usr/local/Nuke12.2v3Nuke12.2", + "--studio" + ] + ] + }, + "environment": { + "__environment_keys__": { + "nukestudio_12.2": [] + } + } + }, "nukestudio_12.0": { "enabled": true, "label": "", @@ -544,6 +622,32 @@ "PYPE_LOG_NO_COLORS": "True" }, "variants": { + "hiero_12.2": { + "enabled": true, + "label": "", + "variant_label": "12.2", + "icon": "", + "executables": { + "windows": [ + [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe", + "--hiero" + ] + ], + "darwin": [], + "linux": [ + [ + "/usr/local/Nuke12.2v3Nuke12.2", + "--hiero" + ] + ] + }, + "environment": { + "__environment_keys__": { + "hiero_12.2": [] + } + } + }, "hiero_12.0": { "enabled": true, "label": "", diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 15b48a636e..8c228e91ef 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -8,16 +8,34 @@ "avalon": [ "AVALON_CONFIG", "AVALON_PROJECTS", + "AVALON_USERNAME", + "AVALON_PASSWORD", + "AVALON_DEBUG", + "AVALON_MONGO", + "AVALON_DB", + "AVALON_DB_DATA", + "AVALON_EARLY_ADOPTER", "AVALON_SCHEMA", + "AVALON_LOCATION", "AVALON_LABEL", - "AVALON_TIMEOUT" + "AVALON_TIMEOUT", + "AVALON_THUMBNAIL_ROOT" ] }, "AVALON_CONFIG": "pype", "AVALON_PROJECTS": "{PYPE_PROJECTS_PATH}", + "AVALON_USERNAME": "avalon", + "AVALON_PASSWORD": "secret", + "AVALON_DEBUG": "1", + "AVALON_MONGO": "mongodb://localhost:2707", + "AVALON_DB": "avalon", + "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", + "AVALON_EARLY_ADOPTER": "1", "AVALON_SCHEMA": "{PYPE_MODULE_ROOT}/schema", + "AVALON_LOCATION": "http://127.0.0.1", "AVALON_LABEL": "Pype", - "AVALON_TIMEOUT": "1000" + "AVALON_TIMEOUT": "1000", + "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails" } }, "Ftrack": { diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_nuke.json b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_nuke.json index d335891b70..c00f8ae266 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_nuke.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/host_settings/template_nuke.json @@ -26,6 +26,11 @@ "type": "schema_template", "name": "template_host_variant", "template_data": [ + { + "host_version": "12.2", + "host_name": "{nuke_type}", + "multipath_executables": true + }, { "host_version": "12.0", "host_name": "{nuke_type}", From a03de3951a7dc0ec13d494057c0b1560f9a85d13 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 22:22:11 +0100 Subject: [PATCH 200/219] remove obsolete settings --- .../defaults/system_settings/hosts.json | 34 ------------------- .../defaults/system_settings/intent.json | 8 ----- 2 files changed, 42 deletions(-) delete mode 100644 pype/settings/defaults/system_settings/hosts.json delete mode 100644 pype/settings/defaults/system_settings/intent.json diff --git a/pype/settings/defaults/system_settings/hosts.json b/pype/settings/defaults/system_settings/hosts.json deleted file mode 100644 index 35ee708df3..0000000000 --- a/pype/settings/defaults/system_settings/hosts.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "blender_2.80": true, - "blender_2.81": true, - "blender_2.82": true, - "blender_2.83": true, - "celaction_local": true, - "celaction_remote": true, - "harmony_17": true, - "maya_2017": true, - "maya_2018": true, - "maya_2019": true, - "maya_2020": true, - "nuke_10.0": true, - "nuke_11.2": true, - "nuke_11.3": true, - "nuke_12.0": true, - "nukex_10.0": true, - "nukex_11.2": true, - "nukex_11.3": true, - "nukex_12.0": true, - "nukestudio_10.0": true, - "nukestudio_11.2": true, - "nukestudio_11.3": true, - "nukestudio_12.0": true, - "houdini_16": true, - "houdini_16.5": true, - "houdini_17": true, - "houdini_18": true, - "premiere_2019": true, - "premiere_2020": true, - "resolve_16": true, - "storyboardpro_7": true, - "unreal_4.24": true -} \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/intent.json b/pype/settings/defaults/system_settings/intent.json deleted file mode 100644 index 844bd1b518..0000000000 --- a/pype/settings/defaults/system_settings/intent.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "items": { - "wip": "WIP", - "test": "TEST", - "final": "FINAL" - }, - "default": "wip" -} \ No newline at end of file From 28d3cfe7f0ffe366b5f69ee35e2ab981ed2157bd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 23:21:47 +0100 Subject: [PATCH 201/219] convert anatomy to settings --- .../defaults/project_anatomy/templates.json | 7 +- .../projects_schema/schema_main.json | 8 +- .../schemas/schema_anatomy_attributes.json | 2 +- .../schemas/schema_anatomy_templates.json | 136 ++++++++++++++++++ 4 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_templates.json diff --git a/pype/settings/defaults/project_anatomy/templates.json b/pype/settings/defaults/project_anatomy/templates.json index 0fff0265b3..32f962556f 100644 --- a/pype/settings/defaults/project_anatomy/templates.json +++ b/pype/settings/defaults/project_anatomy/templates.json @@ -13,9 +13,6 @@ "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", "path": "{@folder}/{@file}" }, - "texture": { - "path": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}" - }, "publish": { "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", @@ -26,5 +23,7 @@ "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/master", "file": "{project[code]}_{asset}_{subset}_master<_{output}><.{frame}>.{representation}", "path": "{@folder}/{@file}" - } + }, + "delivery": {}, + "other": {} } \ No newline at end of file diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json index 837eab0b43..f0226446ea 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_main.json @@ -10,10 +10,10 @@ "type": "anatomy_roots", "key": "roots", "is_file": true - }, { - "type": "anatomy_templates", - "key": "templates", - "is_file": true + }, + { + "type": "schema", + "name": "schema_anatomy_templates" }, { "type": "schema", diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json index 6283100fca..1ede46903c 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -2,7 +2,7 @@ "type": "dict", "collapsable": true, "key": "attributes", - "label": "Attribute Defaults", + "label": "Attributes", "is_file": true, "children": [ { diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_templates.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_templates.json new file mode 100644 index 0000000000..2649178bff --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_anatomy_templates.json @@ -0,0 +1,136 @@ +{ + "type": "dict", + "collapsable": true, + "key": "templates", + "label": "Templates", + "collapsable_key": true, + "is_file": true, + "children": [ + { + "type": "number", + "key": "version_padding", + "label": "Version Padding" + }, + { + "type": "text", + "key": "version", + "label": "Version" + }, + { + "type": "number", + "key": "frame_padding", + "label": "Frame Padding" + }, + { + "type": "text", + "key": "frame", + "label": "Frame" + }, + { + "type": "dict", + "key": "work", + "label": "Work", + "children": [ + { + "type": "text", + "key": "folder", + "label": "Folder" + }, + { + "type": "text", + "key": "file", + "label": "File" + }, + { + "type": "text", + "key": "path", + "label": "Path" + }] + }, + { + "type": "dict", + "key": "render", + "label": "Render", + "children": [ + { + "type": "text", + "key": "folder", + "label": "Folder" + }, + { + "type": "text", + "key": "file", + "label": "File" + }, + { + "type": "text", + "key": "path", + "label": "Path" + } + + ] + }, + { + "type": "dict", + "key": "publish", + "label": "Publish", + "children": [ + { + "type": "text", + "key": "folder", + "label": "Folder" + }, + { + "type": "text", + "key": "file", + "label": "File" + }, + { + "type": "text", + "key": "path", + "label": "Path" + }, + { + "type": "text", + "key": "thumbnail", + "label": "Thumbnail" + }] + }, + { + "type": "dict", + "key": "master", + "label": "Master", + "children": [ + { + "type": "text", + "key": "folder", + "label": "Folder" + }, + { + "type": "text", + "key": "file", + "label": "File" + }, + { + "type": "text", + "key": "path", + "label": "Path" + } + + ] + }, + { + "type": "dict-modifiable", + "key": "delivery", + "label": "Delivery", + "object_type": "text" + }, + { + "type": "dict-modifiable", + "key": "other", + "label": "Other", + "object_type": "text" + } + + ] +} From 80a614261f8837e2208423a320722450523971af Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 23:39:59 +0100 Subject: [PATCH 202/219] maya loade colors --- .../defaults/project_settings/maya.json | 91 ++++--------------- 1 file changed, 18 insertions(+), 73 deletions(-) diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index afc4442d0f..0c640a63e4 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -145,78 +145,23 @@ } }, "load": { - "colors": { - "model": [ - 0.0, - 0.0, - 0.0 - ], - "rig": [ - 0.0, - 0.0, - 0.0 - ], - "pointcache": [ - 0.0, - 0.0, - 0.0 - ], - "animation": [ - 0.0, - 0.0, - 0.0 - ], - "ass": [ - 0.0, - 0.0, - 0.0 - ], - "camera": [ - 0.0, - 0.0, - 0.0 - ], - "fbx": [ - 0.0, - 0.0, - 0.0 - ], - "mayaAscii": [ - 0.0, - 0.0, - 0.0 - ], - "setdress": [ - 0.0, - 0.0, - 0.0 - ], - "layout": [ - 0.0, - 0.0, - 0.0 - ], - "vdbcache": [ - 0.0, - 0.0, - 0.0 - ], - "vrayproxy": [ - 0.0, - 0.0, - 0.0 - ], - "yeticache": [ - 0.0, - 0.0, - 0.0 - ], - "yetiRig": [ - 0.0, - 0.0, - 0.0 - ] - } + "colors": { + "model": [0.821, 0.518, 0.117], + "rig": [0.144, 0.443, 0.463], + "pointcache": [0.368, 0.821, 0.117], + "animation": [0.368, 0.821, 0.117], + "ass": [1.0, 0.332, 0.312], + "camera": [0.447, 0.312, 1.0], + "camerarig": [0.447, 0.312, 1.0], + "fbx": [1.0, 0.931, 0.312], + "mayaAscii": [0.312, 1.0, 0.747], + "setdress": [0.312, 1.0, 0.747], + "layout": [0.312, 1.0, 0.747], + "vdbcache": [0.312, 1.0, 0.428], + "vrayproxy": [0.258, 0.95, 0.541], + "yeticache": [0.2, 0.8, 0.3], + "yetiRig": [0, 0.8, 0.5] + } }, "workfile_build": { "profiles": [ @@ -316,4 +261,4 @@ "ValidateNoAnimation": false } } -} \ No newline at end of file +} From a97cc086edff55867ef8170bab02a7736cad7787 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 10:41:05 +0100 Subject: [PATCH 203/219] settings function names are with verbs now --- pype/api.py | 8 ++--- pype/lib/applications.py | 8 ++--- pype/modules_manager.py | 4 +-- pype/settings/__init__.py | 12 +++---- pype/settings/lib.py | 30 ++++++++-------- pype/tools/settings/settings/widgets/base.py | 36 ++++++++++---------- 6 files changed, 49 insertions(+), 49 deletions(-) diff --git a/pype/api.py b/pype/api.py index b88be4cc88..48a6cfcb92 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,6 +1,6 @@ from .settings import ( - system_settings, - project_settings, + get_system_settings, + get_project_settings, environments ) from pypeapp import ( @@ -50,8 +50,8 @@ from .lib import ( from .lib import _subprocess as subprocess __all__ = [ - "system_settings", - "project_settings", + "get_system_settings", + "get_project_settings", "environments", "Logger", diff --git a/pype/lib/applications.py b/pype/lib/applications.py index ddff9ddcd2..1a5b70f339 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -19,8 +19,8 @@ from ..api import ( Anatomy, Logger, config, - system_settings, - environments + get_system_settings, + get_environments ) from .python_module_tools import ( modules_from_path, @@ -526,7 +526,7 @@ class ApplicationManager: def refresh(self): """Refresh applications from settings.""" - settings = system_settings() + settings = get_system_settings() hosts_definitions = settings["applications"] for app_group, variant_definitions in hosts_definitions.items(): @@ -905,7 +905,7 @@ class ApplicationLaunchContext: # Load settings if were not passed in data settings_env = self.data.get("settings_env") if settings_env is None: - settings_env = environments() + settings_env = get_environments() self.data["settings_env"] = settings_env # subprocess.Popen launch arguments (first argument in constructor) diff --git a/pype/modules_manager.py b/pype/modules_manager.py index 6538187ea9..72023500e4 100644 --- a/pype/modules_manager.py +++ b/pype/modules_manager.py @@ -3,7 +3,7 @@ import inspect import pype.modules from pype.modules import PypeModule -from pype.settings import system_settings +from pype.settings import get_system_settings from pype.api import Logger @@ -24,7 +24,7 @@ class PypeModuleManager: return environments def find_pype_modules(self): - settings = system_settings() + settings = get_system_settings() modules = [] dirpath = os.path.dirname(pype.modules.__file__) for module_name in os.listdir(dirpath): diff --git a/pype/settings/__init__.py b/pype/settings/__init__.py index 7a99ba0b2f..d078815849 100644 --- a/pype/settings/__init__.py +++ b/pype/settings/__init__.py @@ -1,11 +1,11 @@ from .lib import ( - system_settings, - project_settings, - environments + get_system_settings, + get_project_settings, + get_environments ) __all__ = ( - "system_settings", - "project_settings", - "environments" + "get_system_settings", + "get_project_settings", + "get_environments" ) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index ebd0d86df4..0fd2595f3e 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -69,7 +69,7 @@ def reset_default_settings(): _DEFAULT_SETTINGS = None -def default_settings(): +def get_default_settings(): global _DEFAULT_SETTINGS if _DEFAULT_SETTINGS is None: _DEFAULT_SETTINGS = load_jsons_from_dir(DEFAULTS_DIR) @@ -236,21 +236,21 @@ def subkey_merge(_dict, value, keys): return _dict -def studio_system_settings(): +def get_studio_system_settings(): """Studio overrides of system settings.""" if os.path.exists(SYSTEM_SETTINGS_PATH): return load_json_file(SYSTEM_SETTINGS_PATH) return {} -def studio_project_settings(): +def get_studio_project_settings(): """Studio overrides of default project settings.""" if os.path.exists(PROJECT_SETTINGS_PATH): return load_json_file(PROJECT_SETTINGS_PATH) return {} -def studio_project_anatomy(): +def get_studio_project_anatomy(): """Studio overrides of default project anatomy data.""" if os.path.exists(PROJECT_ANATOMY_PATH): return load_json_file(PROJECT_ANATOMY_PATH) @@ -305,8 +305,8 @@ def save_project_settings(project_name, overrides): Do not use to store whole project settings data with defaults but only it's overrides with metadata defining how overrides should be applied in load - function. For loading should be used functions `studio_project_settings` - for global project settings and `project_settings_overrides` for + function. For loading should be used function `get_studio_project_settings` + for global project settings and `get_project_settings_overrides` for project specific settings. Args: @@ -346,7 +346,7 @@ def save_project_anatomy(project_name, anatomy_data): json.dump(anatomy_data, file_stream, indent=4) -def project_settings_overrides(project_name): +def get_project_settings_overrides(project_name): """Studio overrides of project settings for specific project. Args: @@ -412,26 +412,26 @@ def apply_overrides(source_data, override_data): return merge_overrides(_source_data, override_data) -def system_settings(): +def get_system_settings(): """System settings with applied studio overrides.""" - default_values = default_settings()[SYSTEM_SETTINGS_KEY] - studio_values = studio_system_settings() + default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] + studio_values = get_studio_system_settings() return apply_overrides(default_values, studio_values) -def project_settings(project_name): +def get_project_settings(project_name): """Project settings with applied studio and project overrides.""" - default_values = default_settings()[PROJECT_SETTINGS_KEY] - studio_values = studio_project_settings() + default_values = get_default_settings()[PROJECT_SETTINGS_KEY] + studio_values = get_studio_project_settings() studio_overrides = apply_overrides(default_values, studio_values) - project_overrides = project_settings_overrides(project_name) + project_overrides = get_project_settings_overrides(project_name) return apply_overrides(studio_overrides, project_overrides) -def environments(): +def get_environments(): """Calculated environment based on defaults and system settings. Any default environment also found in the system settings will be fully diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 9e8e6537f1..7a43952cfd 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -10,14 +10,14 @@ from pype.settings.lib import ( DEFAULTS_DIR, reset_default_settings, - default_settings, + get_default_settings, - studio_system_settings, - studio_project_settings, - studio_project_anatomy, + get_studio_system_settings, + get_studio_project_settings, + get_studio_project_anatomy, - project_settings_overrides, - project_anatomy_overrides, + get_project_settings_overrides, + get_project_anatomy_overrides, save_studio_settings, save_project_settings, @@ -322,7 +322,7 @@ class SystemWidget(SettingsCategoryWidget): def duplicated_env_group_validation(self, values=None, overrides=None): try: if overrides is not None: - default_values = default_settings()[SYSTEM_SETTINGS_KEY] + default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] values = apply_overrides(default_values, overrides) else: values = copy.deepcopy(values) @@ -375,7 +375,7 @@ class SystemWidget(SettingsCategoryWidget): def update_values(self): default_values = lib.convert_data_to_gui_data({ - self.main_schema_key: default_settings()[SYSTEM_SETTINGS_KEY] + self.main_schema_key: get_default_settings()[SYSTEM_SETTINGS_KEY] }) for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -384,7 +384,7 @@ class SystemWidget(SettingsCategoryWidget): system_values = lib.NOT_SET else: system_values = lib.convert_overrides_to_gui_data( - {self.main_schema_key: studio_system_settings()} + {self.main_schema_key: get_studio_system_settings()} ) for input_field in self.input_fields: @@ -549,8 +549,8 @@ class ProjectWidget(SettingsCategoryWidget): _project_anatomy = lib.NOT_SET self.is_overidable = False else: - _project_overrides = project_settings_overrides(project_name) - _project_anatomy = project_anatomy_overrides(project_name) + _project_overrides = get_project_settings_overrides(project_name) + _project_anatomy = get_project_anatomy_overrides(project_name) self.is_overidable = True overrides = {self.main_schema_key: { @@ -590,16 +590,16 @@ class ProjectWidget(SettingsCategoryWidget): project_anatomy_data = output_data.get(PROJECT_ANATOMY_KEY, {}) save_project_anatomy(self.project_name, project_anatomy_data) - if self.project_name: - # Refill values with overrides - self._on_project_change() - else: + if studio_overrides: # Update saved values self._update_values() + else: + # Refill values with overrides + self._on_project_change() def update_values(self): default_values = lib.convert_data_to_gui_data( - {self.main_schema_key: default_settings()} + {self.main_schema_key: get_default_settings()} ) for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -609,8 +609,8 @@ class ProjectWidget(SettingsCategoryWidget): else: studio_values = lib.convert_overrides_to_gui_data({ self.main_schema_key: { - PROJECT_SETTINGS_KEY: studio_project_settings(), - PROJECT_ANATOMY_KEY: studio_project_anatomy() + PROJECT_SETTINGS_KEY: get_studio_project_settings(), + PROJECT_ANATOMY_KEY: get_studio_project_anatomy() } }) From 0685c98c77bf92882a408e9233ef9bce6bfa9bb1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 10:41:19 +0100 Subject: [PATCH 204/219] fixed json decode exception for python 2 --- pype/settings/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 0fd2595f3e..df4a19d7b4 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -5,6 +5,9 @@ import copy log = logging.getLogger(__name__) +# Py2 + Py3 json decode exception +JSON_EXC = getattr(json.decoder, "JSONDecodeError", ValueError) + # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" # Metadata key for storing information about environments @@ -82,7 +85,7 @@ def load_json_file(fpath): with open(fpath, "r") as opened_file: return json.load(opened_file) - except json.decoder.JSONDecodeError: + except JSON_EXC: log.warning( "File has invalid json format \"{}\"".format(fpath), exc_info=True From 7db9a0ca9d9001d7528928f3ed23dde1a3bfc173 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 10:43:12 +0100 Subject: [PATCH 205/219] get_environments skip deprecated environemnts loading --- pype/settings/lib.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index df4a19d7b4..a66e14e28e 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -443,11 +443,5 @@ def get_environments(): Returns: dict: Output should be ready for `acre` module. """ - # TODO remove these defaults (All should be set with system settings) - envs = copy.deepcopy(default_settings()[ENVIRONMENTS_KEY]) - # This is part of loading environments from settings - envs_from_system_settings = find_environments(system_settings()) - for env_group_key, values in envs_from_system_settings.items(): - envs[env_group_key] = values - return envs + return find_environments(get_system_settings()) From 79b4cc9f115d266113505e44536b07fc87a69a90 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 10:43:44 +0100 Subject: [PATCH 206/219] added function `get_anatomy_data` to get anatomy data --- pype/settings/lib.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index a66e14e28e..9ca9872958 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -367,7 +367,7 @@ def get_project_settings_overrides(project_name): return load_json_file(path_to_json) -def project_anatomy_overrides(project_name): +def get_project_anatomy_overrides(project_name): """Studio overrides of project anatomy for specific project. Args: @@ -434,6 +434,18 @@ def get_project_settings(project_name): return apply_overrides(studio_overrides, project_overrides) +def get_anatomy_data(project_name): + """Project anatomy data with applied studio and project overrides.""" + default_values = get_default_settings()[PROJECT_ANATOMY_KEY] + studio_values = get_studio_project_anatomy() + + studio_overrides = apply_overrides(default_values, studio_values) + + project_overrides = get_project_anatomy_overrides(project_name) + + return apply_overrides(studio_overrides, project_overrides) + + def get_environments(): """Calculated environment based on defaults and system settings. From 2dadab864320b57bc1a7cc5447cf1ee6263047ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:00:09 +0100 Subject: [PATCH 207/219] fixed api imports --- pype/api.py | 6 ++++-- pype/settings/__init__.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/api.py b/pype/api.py index 48a6cfcb92..34e2c79845 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,7 +1,8 @@ from .settings import ( get_system_settings, get_project_settings, - environments + get_anatomy_data, + get_environments ) from pypeapp import ( Logger, @@ -52,7 +53,8 @@ from .lib import _subprocess as subprocess __all__ = [ "get_system_settings", "get_project_settings", - "environments", + "get_anatomy_data", + "get_environments", "Logger", "Anatomy", diff --git a/pype/settings/__init__.py b/pype/settings/__init__.py index d078815849..2f487038bd 100644 --- a/pype/settings/__init__.py +++ b/pype/settings/__init__.py @@ -1,11 +1,13 @@ from .lib import ( get_system_settings, get_project_settings, + get_anatomy_data, get_environments ) __all__ = ( "get_system_settings", "get_project_settings", + "get_anatomy_data", "get_environments" ) From 8e1f1fc7ccd97edfbe655cc168e1791100e11c93 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:19:13 +0100 Subject: [PATCH 208/219] make cleaner get overrides function names --- pype/settings/lib.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 9ca9872958..0cdab1d7b5 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -239,27 +239,6 @@ def subkey_merge(_dict, value, keys): return _dict -def get_studio_system_settings(): - """Studio overrides of system settings.""" - if os.path.exists(SYSTEM_SETTINGS_PATH): - return load_json_file(SYSTEM_SETTINGS_PATH) - return {} - - -def get_studio_project_settings(): - """Studio overrides of default project settings.""" - if os.path.exists(PROJECT_SETTINGS_PATH): - return load_json_file(PROJECT_SETTINGS_PATH) - return {} - - -def get_studio_project_anatomy(): - """Studio overrides of default project anatomy data.""" - if os.path.exists(PROJECT_ANATOMY_PATH): - return load_json_file(PROJECT_ANATOMY_PATH) - return {} - - def path_to_project_settings(project_name): if not project_name: return PROJECT_SETTINGS_PATH @@ -349,6 +328,27 @@ def save_project_anatomy(project_name, anatomy_data): json.dump(anatomy_data, file_stream, indent=4) +def get_studio_overrides_system_settings(): + """Studio overrides of system settings.""" + if os.path.exists(SYSTEM_SETTINGS_PATH): + return load_json_file(SYSTEM_SETTINGS_PATH) + return {} + + +def get_studio_overrides_default_project_settings(): + """Studio overrides of default project settings.""" + if os.path.exists(PROJECT_SETTINGS_PATH): + return load_json_file(PROJECT_SETTINGS_PATH) + return {} + + +def get_studio_overrides_default_project_anatomy(): + """Studio overrides of default project anatomy data.""" + if os.path.exists(PROJECT_ANATOMY_PATH): + return load_json_file(PROJECT_ANATOMY_PATH) + return {} + + def get_project_settings_overrides(project_name): """Studio overrides of project settings for specific project. From 09a62d805012f443be29f52269a364f377fd81db Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:19:50 +0100 Subject: [PATCH 209/219] added get_default_project_anatomy_data and get_default_project_settings --- pype/settings/lib.py | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 0cdab1d7b5..1b98ce19aa 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -418,34 +418,54 @@ def apply_overrides(source_data, override_data): def get_system_settings(): """System settings with applied studio overrides.""" default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] - studio_values = get_studio_system_settings() + studio_values = get_studio_overrides_system_settings() return apply_overrides(default_values, studio_values) -def get_project_settings(project_name): - """Project settings with applied studio and project overrides.""" +def get_default_project_settings(): + """Project settings with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_SETTINGS_KEY] - studio_values = get_studio_project_settings() + studio_values = get_studio_overrides_default_project_settings() - studio_overrides = apply_overrides(default_values, studio_values) + return apply_overrides(default_values, studio_values) - project_overrides = get_project_settings_overrides(project_name) - return apply_overrides(studio_overrides, project_overrides) +def get_default_project_anatomy_data(): + """Project anatomy data with applied studio's default project overrides.""" + default_values = get_default_settings()[PROJECT_ANATOMY_KEY] + studio_values = get_studio_overrides_default_project_anatomy() + + return apply_overrides(default_values, studio_values) def get_anatomy_data(project_name): """Project anatomy data with applied studio and project overrides.""" - default_values = get_default_settings()[PROJECT_ANATOMY_KEY] - studio_values = get_studio_project_anatomy() - - studio_overrides = apply_overrides(default_values, studio_values) + if not project_name: + raise ValueError( + "Must enter project name." + " Call `get_default_project_anatomy_data` to get project defaults." + ) + studio_overrides = get_default_project_anatomy_data() project_overrides = get_project_anatomy_overrides(project_name) return apply_overrides(studio_overrides, project_overrides) +def get_project_settings(project_name): + """Project settings with applied studio and project overrides.""" + if not project_name: + raise ValueError( + "Must enter project name." + " Call `get_default_project_settings` to get project defaults." + ) + + studio_overrides = get_default_project_settings() + project_overrides = get_project_settings_overrides(project_name) + + return apply_overrides(studio_overrides, project_overrides) + + def get_environments(): """Calculated environment based on defaults and system settings. From a5c5e7262c5ceeb94edba32ed3f5fd876e7aa60a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:20:05 +0100 Subject: [PATCH 210/219] setting gui use new overrides functions --- pype/tools/settings/settings/widgets/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 7a43952cfd..ec86da74c4 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -12,9 +12,9 @@ from pype.settings.lib import ( reset_default_settings, get_default_settings, - get_studio_system_settings, - get_studio_project_settings, - get_studio_project_anatomy, + get_studio_overrides_system_settings, + get_studio_overrides_default_project_settings, + get_studio_overrides_default_project_anatomy, get_project_settings_overrides, get_project_anatomy_overrides, @@ -384,7 +384,7 @@ class SystemWidget(SettingsCategoryWidget): system_values = lib.NOT_SET else: system_values = lib.convert_overrides_to_gui_data( - {self.main_schema_key: get_studio_system_settings()} + {self.main_schema_key: get_studio_overrides_system_settings()} ) for input_field in self.input_fields: @@ -609,8 +609,12 @@ class ProjectWidget(SettingsCategoryWidget): else: studio_values = lib.convert_overrides_to_gui_data({ self.main_schema_key: { - PROJECT_SETTINGS_KEY: get_studio_project_settings(), - PROJECT_ANATOMY_KEY: get_studio_project_anatomy() + PROJECT_SETTINGS_KEY: ( + get_studio_overrides_default_project_settings() + ), + PROJECT_ANATOMY_KEY: ( + get_studio_overrides_default_project_anatomy() + ) } }) From a562e0c3bc1f3132bc538eed38f385ff6c69ab2f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:22:46 +0100 Subject: [PATCH 211/219] fixed docstring --- pype/settings/lib.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 1b98ce19aa..0b830dd5ab 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -287,9 +287,9 @@ def save_project_settings(project_name, overrides): Do not use to store whole project settings data with defaults but only it's overrides with metadata defining how overrides should be applied in load - function. For loading should be used function `get_studio_project_settings` - for global project settings and `get_project_settings_overrides` for - project specific settings. + function. For loading should be used function + `get_studio_overrides_default_project_settings` for global project settings + and `get_project_settings_overrides` for project specific settings. Args: project_name(str, null): Project name for which overrides are @@ -358,8 +358,6 @@ def get_project_settings_overrides(project_name): Returns: dict: Only overrides for entered project, may be empty dictionary. """ - if not project_name: - return {} path_to_json = path_to_project_settings(project_name) if not os.path.exists(path_to_json): From 025cf4e8cc2c9a2a075f02b5c0e3676d261f25c3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 11:23:30 +0100 Subject: [PATCH 212/219] added function to get current context project settings `get_current_project_settings` --- pype/api.py | 2 ++ pype/settings/__init__.py | 2 ++ pype/settings/lib.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/pype/api.py b/pype/api.py index 34e2c79845..3854da6d30 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,6 +1,7 @@ from .settings import ( get_system_settings, get_project_settings, + get_current_project_settings, get_anatomy_data, get_environments ) @@ -53,6 +54,7 @@ from .lib import _subprocess as subprocess __all__ = [ "get_system_settings", "get_project_settings", + "get_current_project_settings", "get_anatomy_data", "get_environments", diff --git a/pype/settings/__init__.py b/pype/settings/__init__.py index 2f487038bd..479236b99a 100644 --- a/pype/settings/__init__.py +++ b/pype/settings/__init__.py @@ -1,6 +1,7 @@ from .lib import ( get_system_settings, get_project_settings, + get_current_project_settings, get_anatomy_data, get_environments ) @@ -8,6 +9,7 @@ from .lib import ( __all__ = ( "get_system_settings", "get_project_settings", + "get_current_project_settings", "get_anatomy_data", "get_environments" ) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 0b830dd5ab..23e303556e 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -464,6 +464,22 @@ def get_project_settings(project_name): return apply_overrides(studio_overrides, project_overrides) +def get_current_project_settings(): + """Project settings for current context project. + + Project name should be stored in environment variable `AVALON_PROJECT`. + This function should be used only in host context where environment + variable must be set and should not happen that any part of process will + change the value of the enviornment variable. + """ + project_name = os.environ.get("AVALON_PROJECT") + if not project_name: + raise ValueError( + "Missing context project in environemt variable `AVALON_PROJECT`." + ) + return get_project_settings(project_name) + + def get_environments(): """Calculated environment based on defaults and system settings. From ca7663bbc9f052c06da2d5346500d83b8472c2e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 15:53:04 +0100 Subject: [PATCH 213/219] renamed overrides getter function --- pype/settings/lib.py | 12 ++++++------ pype/tools/settings/settings/widgets/base.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 23e303556e..0856e7048a 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -288,7 +288,7 @@ def save_project_settings(project_name, overrides): Do not use to store whole project settings data with defaults but only it's overrides with metadata defining how overrides should be applied in load function. For loading should be used function - `get_studio_overrides_default_project_settings` for global project settings + `get_studio_project_settings_overrides` for global project settings and `get_project_settings_overrides` for project specific settings. Args: @@ -328,21 +328,21 @@ def save_project_anatomy(project_name, anatomy_data): json.dump(anatomy_data, file_stream, indent=4) -def get_studio_overrides_system_settings(): +def get_studio_system_settings_overrides(): """Studio overrides of system settings.""" if os.path.exists(SYSTEM_SETTINGS_PATH): return load_json_file(SYSTEM_SETTINGS_PATH) return {} -def get_studio_overrides_default_project_settings(): +def get_studio_project_settings_overrides(): """Studio overrides of default project settings.""" if os.path.exists(PROJECT_SETTINGS_PATH): return load_json_file(PROJECT_SETTINGS_PATH) return {} -def get_studio_overrides_default_project_anatomy(): +def get_studio_project_anatomy_overrides(): """Studio overrides of default project anatomy data.""" if os.path.exists(PROJECT_ANATOMY_PATH): return load_json_file(PROJECT_ANATOMY_PATH) @@ -416,14 +416,14 @@ def apply_overrides(source_data, override_data): def get_system_settings(): """System settings with applied studio overrides.""" default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] - studio_values = get_studio_overrides_system_settings() + studio_values = get_studio_system_settings_overrides() return apply_overrides(default_values, studio_values) def get_default_project_settings(): """Project settings with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_SETTINGS_KEY] - studio_values = get_studio_overrides_default_project_settings() + studio_values = get_studio_project_settings_overrides() return apply_overrides(default_values, studio_values) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index ec86da74c4..ae4d131265 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -12,8 +12,8 @@ from pype.settings.lib import ( reset_default_settings, get_default_settings, - get_studio_overrides_system_settings, - get_studio_overrides_default_project_settings, + get_studio_system_settings_overrides, + get_studio_project_settings_overrides, get_studio_overrides_default_project_anatomy, get_project_settings_overrides, @@ -384,7 +384,7 @@ class SystemWidget(SettingsCategoryWidget): system_values = lib.NOT_SET else: system_values = lib.convert_overrides_to_gui_data( - {self.main_schema_key: get_studio_overrides_system_settings()} + {self.main_schema_key: get_studio_system_settings_overrides()} ) for input_field in self.input_fields: @@ -610,7 +610,7 @@ class ProjectWidget(SettingsCategoryWidget): studio_values = lib.convert_overrides_to_gui_data({ self.main_schema_key: { PROJECT_SETTINGS_KEY: ( - get_studio_overrides_default_project_settings() + get_studio_project_settings_overrides() ), PROJECT_ANATOMY_KEY: ( get_studio_overrides_default_project_anatomy() From ab6482f61b64816b86b2f611a44017ad317d5aa6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 15:54:09 +0100 Subject: [PATCH 214/219] fix last functio name --- pype/tools/settings/settings/widgets/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index ae4d131265..5127ea9f88 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -14,7 +14,7 @@ from pype.settings.lib import ( get_studio_system_settings_overrides, get_studio_project_settings_overrides, - get_studio_overrides_default_project_anatomy, + get_studio_project_anatomy_overrides, get_project_settings_overrides, get_project_anatomy_overrides, @@ -613,7 +613,7 @@ class ProjectWidget(SettingsCategoryWidget): get_studio_project_settings_overrides() ), PROJECT_ANATOMY_KEY: ( - get_studio_overrides_default_project_anatomy() + get_studio_project_anatomy_overrides() ) } }) From 41ce3ea1905d92d7ca9a9d18e69382193a65006e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 15:57:43 +0100 Subject: [PATCH 215/219] last fix! --- pype/settings/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 0856e7048a..33a427c3a6 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -431,7 +431,7 @@ def get_default_project_settings(): def get_default_project_anatomy_data(): """Project anatomy data with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_ANATOMY_KEY] - studio_values = get_studio_overrides_default_project_anatomy() + studio_values = get_studio_project_anatomy_overrides() return apply_overrides(default_values, studio_values) From 31a2c70b26c7cce67806334c35352e3bb2ccf0d4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Dec 2020 19:29:13 +0100 Subject: [PATCH 216/219] implemented `clear_metadata_from_settings` to remove metadata from loaded settings --- pype/settings/lib.py | 61 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 33a427c3a6..1189b1ab2a 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -17,6 +17,13 @@ M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__" # NOTE key popping not implemented yet M_POP_KEY = "__pop_key__" +METADATA_KEYS = ( + M_OVERRIDEN_KEY, + M_ENVIRONMENT_KEY, + M_DYNAMIC_KEY_LABEL, + M_POP_KEY +) + # Folder where studio overrides are stored STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") or "" @@ -413,30 +420,37 @@ def apply_overrides(source_data, override_data): return merge_overrides(_source_data, override_data) -def get_system_settings(): +def get_system_settings(clear_metadata=True): """System settings with applied studio overrides.""" default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] studio_values = get_studio_system_settings_overrides() - return apply_overrides(default_values, studio_values) + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_default_project_settings(): +def get_default_project_settings(clear_metadata=True): """Project settings with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_SETTINGS_KEY] studio_values = get_studio_project_settings_overrides() - - return apply_overrides(default_values, studio_values) + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_default_project_anatomy_data(): +def get_default_project_anatomy_data(clear_metadata=True): """Project anatomy data with applied studio's default project overrides.""" default_values = get_default_settings()[PROJECT_ANATOMY_KEY] studio_values = get_studio_project_anatomy_overrides() - - return apply_overrides(default_values, studio_values) + result = apply_overrides(default_values, studio_values) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_anatomy_data(project_name): +def get_anatomy_data(project_name, clear_metadata=True): """Project anatomy data with applied studio and project overrides.""" if not project_name: raise ValueError( @@ -444,13 +458,16 @@ def get_anatomy_data(project_name): " Call `get_default_project_anatomy_data` to get project defaults." ) - studio_overrides = get_default_project_anatomy_data() + studio_overrides = get_default_project_anatomy_data(False) project_overrides = get_project_anatomy_overrides(project_name) - return apply_overrides(studio_overrides, project_overrides) + result = apply_overrides(studio_overrides, project_overrides) + if clear_metadata: + clear_metadata_from_settings(result) + return result -def get_project_settings(project_name): +def get_project_settings(project_name, clear_metadata=True): """Project settings with applied studio and project overrides.""" if not project_name: raise ValueError( @@ -458,10 +475,13 @@ def get_project_settings(project_name): " Call `get_default_project_settings` to get project defaults." ) - studio_overrides = get_default_project_settings() + studio_overrides = get_default_project_settings(False) project_overrides = get_project_settings_overrides(project_name) - return apply_overrides(studio_overrides, project_overrides) + result = apply_overrides(studio_overrides, project_overrides) + if clear_metadata: + clear_metadata_from_settings(result) + return result def get_current_project_settings(): @@ -491,3 +511,16 @@ def get_environments(): """ return find_environments(get_system_settings()) + + +def clear_metadata_from_settings(values): + """Remove all metadata keys from loaded settings.""" + if isinstance(values, dict): + for key in tuple(values.keys()): + if key in METADATA_KEYS: + values.pop(key) + else: + clear_metadata_from_settings(values[key]) + elif isinstance(values, list): + for item in values: + clear_metadata_from_settings(item) From df76d931a1b0a9076074d9a0d25ce2c2975bdb53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 10:57:53 +0100 Subject: [PATCH 217/219] AE - fix publish after background load --- pype/lib/__init__.py | 9 ++- pype/lib/plugin_tools.py | 56 ++++++++++++++++++ .../stubs/aftereffects_server_stub.py | 13 ++++- .../aftereffects/create/create_render.py | 2 +- .../aftereffects/load/load_background.py | 3 +- pype/plugins/aftereffects/load/load_file.py | 2 +- .../aftereffects/publish/collect_render.py | 6 +- pype/plugins/lib.py | 57 ------------------- 8 files changed, 84 insertions(+), 64 deletions(-) delete mode 100644 pype/plugins/lib.py diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 1ade97cd0e..78fd69da98 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -25,7 +25,12 @@ from .applications import ( _subprocess ) -from .plugin_tools import filter_pyblish_plugins, source_hash +from .plugin_tools import ( + filter_pyblish_plugins, + source_hash, + get_unique_layer_name, + get_background_layers +) from .path_tools import ( version_up, @@ -57,6 +62,8 @@ __all__ = [ "ApplicationAction", "filter_pyblish_plugins", + "get_unique_layer_name", + "get_background_layers", "version_up", "get_version_from_path", diff --git a/pype/lib/plugin_tools.py b/pype/lib/plugin_tools.py index 0b6ace807e..589aaa58f6 100644 --- a/pype/lib/plugin_tools.py +++ b/pype/lib/plugin_tools.py @@ -3,6 +3,8 @@ import os import inspect import logging +import re +import json from ..api import config @@ -78,3 +80,57 @@ def source_hash(filepath, *args): time = str(os.path.getmtime(filepath)) size = str(os.path.getsize(filepath)) return "|".join([file_name, time, size] + list(args)).replace(".", ",") + + +def get_unique_layer_name(layers, name): + """ + Gets all layer names and if 'name' is present in them, increases + suffix by 1 (eg. creates unique layer name - for Loader) + Args: + layers (list): of strings, names only + name (string): checked value + + Returns: + (string): name_00X (without version) + """ + names = {} + for layer in layers: + layer_name = re.sub(r'_\d{3}$', '', layer) + if layer_name in names.keys(): + names[layer_name] = names[layer_name] + 1 + else: + names[layer_name] = 1 + occurrences = names.get(name, 0) + + return "{}_{:0>3d}".format(name, occurrences + 1) + + +def get_background_layers(file_url): + """ + Pulls file name from background json file, enrich with folder url for + AE to be able import files. + + Order is important, follows order in json. + + Args: + file_url (str): abs url of background json + + Returns: + (list): of abs paths to images + """ + with open(file_url) as json_file: + data = json.load(json_file) + + layers = list() + bg_folder = os.path.dirname(file_url) + for child in data['children']: + if child.get("filename"): + layers.append(os.path.join(bg_folder, child.get("filename")). + replace("\\", "/")) + else: + for layer in child['children']: + if layer.get("filename"): + layers.append(os.path.join(bg_folder, + layer.get("filename")). + replace("\\", "/")) + return layers \ No newline at end of file diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index d4c1cf4512..9449d0b378 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -23,6 +23,10 @@ class AEItem(object): # all imported elements, single for # regular image, array for Backgrounds members = attr.ib(factory=list) + workAreaStart = attr.ib(default=None) + workAreaDuration = attr.ib(default=None) + frameRate = attr.ib(default=None) + file_name = attr.ib(default=None) class AfterEffectsServerStub(): @@ -66,7 +70,7 @@ class AfterEffectsServerStub(): metadata = json.loads(res) except json.decoder.JSONDecodeError: raise ValueError("Unparsable metadata {}".format(res)) - return metadata or {} + return metadata or [] def read(self, item, layers_meta=None): """ @@ -488,6 +492,11 @@ class AfterEffectsServerStub(): item = AEItem(d.get('id'), d.get('name'), d.get('type'), - d.get('members')) + d.get('members'), + d.get('workAreaStart'), + d.get('workAreaDuration'), + d.get('frameRate'), + d.get('file_name')) + ret.append(item) return ret diff --git a/pype/plugins/aftereffects/create/create_render.py b/pype/plugins/aftereffects/create/create_render.py index 1944cf9937..6d876e349d 100644 --- a/pype/plugins/aftereffects/create/create_render.py +++ b/pype/plugins/aftereffects/create/create_render.py @@ -35,7 +35,7 @@ class CreateRender(api.Creator): if self.name.lower() == item.name.lower(): self._show_msg(txt) return False - + self.data["members"] = [item.id] stub.imprint(item, self.data) stub.set_label_color(item.id, 14) # Cyan options 0 - 16 stub.rename_item(item, self.data["subset"]) diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index 14f75c2850..fc23d5098e 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -2,7 +2,7 @@ import re from avalon import api, aftereffects -from pype.plugins.lib import get_background_layers, get_unique_layer_name +from pype.lib import get_background_layers, get_unique_layer_name stub = aftereffects.stub() @@ -51,6 +51,7 @@ class BackgroundLoader(api.Loader): def update(self, container, representation): """ Switch asset or change version """ context = representation.get("context", {}) + layer = container.pop("layer") # without iterator number (_001, 002...) namespace_from_container = re.sub(r'_\d{3}$', '', diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 2e07e933f7..ba11856678 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -1,5 +1,5 @@ from avalon import api, aftereffects -from pype.plugins import lib +from pype import lib import re stub = aftereffects.stub() diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index 2dd447d03e..7f7d5a52bc 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -33,12 +33,16 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): compositions = aftereffects.stub().get_items(True) compositions_by_id = {item.id: item for item in compositions} - for item_id, inst in aftereffects.stub().get_metadata().items(): + for inst in aftereffects.stub().get_metadata(): schema = inst.get('schema') # loaded asset container skip it if schema and 'container' in schema: continue + if not inst["members"]: + raise ValueError("Couldn't find id, unable to publish. " + + "Please recreate instance.") + item_id = inst["members"][0] work_area_info = aftereffects.stub().get_work_area(int(item_id)) frameStart = work_area_info.workAreaStart diff --git a/pype/plugins/lib.py b/pype/plugins/lib.py deleted file mode 100644 index c0a4934439..0000000000 --- a/pype/plugins/lib.py +++ /dev/null @@ -1,57 +0,0 @@ -import re -import json -import os - - -def get_unique_layer_name(layers, name): - """ - Gets all layer names and if 'name' is present in them, increases - suffix by 1 (eg. creates unique layer name - for Loader) - Args: - layers (list): of strings, names only - name (string): checked value - - Returns: - (string): name_00X (without version) - """ - names = {} - for layer in layers: - layer_name = re.sub(r'_\d{3}$', '', layer) - if layer_name in names.keys(): - names[layer_name] = names[layer_name] + 1 - else: - names[layer_name] = 1 - occurrences = names.get(name, 0) - - return "{}_{:0>3d}".format(name, occurrences + 1) - - -def get_background_layers(file_url): - """ - Pulls file name from background json file, enrich with folder url for - AE to be able import files. - - Order is important, follows order in json. - - Args: - file_url (str): abs url of background json - - Returns: - (list): of abs paths to images - """ - with open(file_url) as json_file: - data = json.load(json_file) - - layers = list() - bg_folder = os.path.dirname(file_url) - for child in data['children']: - if child.get("filename"): - layers.append(os.path.join(bg_folder, child.get("filename")). - replace("\\", "/")) - else: - for layer in child['children']: - if layer.get("filename"): - layers.append(os.path.join(bg_folder, - layer.get("filename")). - replace("\\", "/")) - return layers From 4d742f50e21f3de4274a1b298188b0455495193d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 11:40:27 +0100 Subject: [PATCH 218/219] Hound --- pype/lib/plugin_tools.py | 2 +- pype/plugins/aftereffects/load/load_background.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/plugin_tools.py b/pype/lib/plugin_tools.py index 589aaa58f6..f5eb354ca3 100644 --- a/pype/lib/plugin_tools.py +++ b/pype/lib/plugin_tools.py @@ -133,4 +133,4 @@ def get_background_layers(file_url): layers.append(os.path.join(bg_folder, layer.get("filename")). replace("\\", "/")) - return layers \ No newline at end of file + return layers diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index fc23d5098e..879734e4f9 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -51,7 +51,7 @@ class BackgroundLoader(api.Loader): def update(self, container, representation): """ Switch asset or change version """ context = representation.get("context", {}) - layer = container.pop("layer") + _ = container.pop("layer") # without iterator number (_001, 002...) namespace_from_container = re.sub(r'_\d{3}$', '', From c1133a9b6d242ce7245b4f54641dc7386c246033 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 18:50:40 +0100 Subject: [PATCH 219/219] Fix - json metadata and rendered files should be in same folder --- .../publish/submit_aftereffects_deadline.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py index 9414bdd39d..5e5c00dec1 100644 --- a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py +++ b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py @@ -105,3 +105,13 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline deadline_plugin_info.Output = render_path.replace("\\", "/") return attr.asdict(deadline_plugin_info) + + def from_published_scene(self): + """ Do not overwrite expected files. + + Use published is set to True, so rendering will be triggered + from published scene (in 'publish' folder). Default implementation + of abstract class renames expected (eg. rendered) files accordingly + which is not needed here. + """ + return super().from_published_scene(False)