├── LICENSE ├── README.md ├── WrapItUp 汉化 Chinese Translations ├── WrapItUp.py └── menu.py └── WrapItUp ├── WrapItUp.py └── menu.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Max van Leeuwen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![nuke collect all files for nuke script](https://maxvanleeuwen.com/wp-content/uploads/WrapItUp_nuke.png) 3 | 4 | ![collect nuke get all media for nukescript](https://maxvanleeuwen.com/wp-content/uploads/WrapItUp_CollectedMedia.png) 5 | 6 | 7 |

8 | 9 | # WrapItUp 10 | 11 | WrapItUp is an easy-to-use tool that can collect and relink almost all files required to make a Nuke script work on another machine. It has a user interface if you run it in Nuke, it can be executed from a command-line window for batch operations, and it can be called using a Python function. 12 | 13 | I made sure to cram the entire thing in just one Python file, so you can simply drag-and-drop the file into Nuke's script editor if you wish to use it just once. To quickly collect all files for a nuke file, simply choose an empty folder in the “collection folder” box at the top of the window and hit Start. 14 | 15 | Tested on Windows, Mac OS, and Linux (CentOS). 16 | 17 |

18 | 19 | ## Features and Settings 20 | 21 | - Three copies of the Nuke script will be collected: the original, a relinked one, and a relative-relinked one (using a python-embedded TCL expression). The relative-relinked Nuke script will stay linked to its media, even if you move it to a completely different machine - as long as the media is right next to it. 22 | - Media files with the path variables ####..., %**d (printf), %v and %V are supported. 23 | - Gizmos will be collected, and the necessary menu.py and init.py files will be generated. Simply place these files in your .nuke folder to install them. 24 | - A detailed CSV log file is generated to review which files were copied. 25 | 26 | **Not supported:** Shared library plugins, (.dll, .dylib and .so files). There are simply too many dependencies and licenses involved to keep track of these. 27 | 28 | See [my website (maxvanleeuwen.com/wrapitup)](https://maxvanleeuwen.com/wrapitup) for more information about all features and settings in this script, and for command-line/Python use! 29 | 30 |

31 | 32 | ## Installation 33 | 34 |

35 | 36 | ### Standard Nuke Installation 37 | 38 | 1. Place the WrapItUp folder in your .nuke folder (or somewhere else on your computer). 39 | 2. Go to your .nuke folder, and create a file called 'init.py'. If such a file already exists, open it. 40 | 3. In the init.py file, add this line of text to the end and save it: 41 | ```python 42 | nuke.pluginAddPath('./WrapItUp') 43 | ``` 44 | 45 | If you want to place the folder somewhere else than in the .nuke folder, make sure to change the path in the init.py file so that it points to that other path instead! 46 | 47 |

48 | 49 | ### Only Run Once (in Nuke) 50 | 51 | If you are only running it once, you can simply drag-and-drop the WrapItUp/WrapItUp.py file into the script editor panel in Nuke and click 'Run the script'. 52 | 53 |

54 | 55 | ### Installation using NukeShared 56 | 57 | 1. Place the WrapItUp folder in the '_AutoInstaller' repository. 58 | 59 | NukeShared is a way of installing plugins by dragging/dropping them in folders, see [this page](https://maxvanleeuwen.com/nukeshared) for more information. 60 | 61 | If you encounter any issues (or if you have feedback/ideas), [let me know (maxvanleeuwen.com/contact)](https://maxvanleeuwen.com/contact)! 62 | 63 |

64 | 65 | ## Updates 66 | 67 | ### 2.1 68 | 69 | - Added maximum of 5 node names in a path when media is reused (to prevent path name length overflow) 70 | 71 | ### 2.0 72 | 73 | - Added -d flag in terminal to skip all disabled nodes, and (*) in UI list to show when nodes are disabled 74 | 75 | ### 1.9 76 | 77 | - Fixed Nuke 13 (Python 3) compatibility 78 | 79 | ### 1.8 80 | 81 | - Fixed Nuke 10.5 compatibility 82 | 83 | ### 1.7 84 | 85 | - Added support for media in Project Directory (root settings) 86 | - Added interactive license (nuke_i) support (for internal use, when scripts are relinked) 87 | - Added more information to log CSV 88 | - Fixed bug with font folder relinking 89 | - Fixed bug with 0b files freezing the copying process 90 | 91 | ### 1.6 92 | 93 | - Relinking and relative relinking now only modifies knobs of media that is not user-ignored 94 | 95 | ### 1.5 96 | 97 | - Fixed 'open folder' keyboard shortcut 98 | - Code cleanup 99 | 100 | ### 1.4 101 | 102 | - Added 'go to node' button 103 | - Fixed error on empty project font path 104 | 105 | ### 1.3 106 | 107 | - Fixed TCL support on font paths 108 | - Fixed issue with gizmo's and nuke script containing dots in their names 109 | - UI labels now evaluate TCL as well 110 | - Log now stores used nuke version 111 | 112 | ### 1.2 113 | 114 | - File paths with TCL are supported 115 | - Folder browser will ask to create new folders if the selected path does not exist 116 | 117 | ### 1.1 118 | 119 | - Added PySide support (for Nuke 10.5 and older) 120 | - Fixed Nuke closing on user interruption when 'exit on finish' is checked 121 | - Fixed Nuke asking to save your Nuke script when 'exit on finish' is checked and script has not been saved 122 | 123 | ### 1.0 124 | 125 | - Initial release 126 | -------------------------------------------------------------------------------- /WrapItUp 汉化 Chinese Translations/WrapItUp.py: -------------------------------------------------------------------------------- 1 | #encoding=utf-8 2 | # Max van Leeuwen - maxvanleeuwen.com/WrapItUp 3 | # WrapItUp - 2.1 4 | # 5 | # Collect all media, gizmos and files associated with a nuke script, and copy it all to a separate folder - along with a relinked duplicate of the nuke script. 6 | 7 | 8 | 9 | WIU_Title = 'WrapItUp 2.1 - maxvanleeuwen.com' 10 | WIU_Log = '[WrapItUp] ' 11 | 12 | 13 | 14 | # import PySide(2) 15 | try: 16 | 17 | # try importing PySide2 18 | try: 19 | import PySide2.QtCore as QtCore 20 | import PySide2.QtGui as QtGui 21 | import PySide2.QtWidgets as QtWidgets 22 | 23 | # on error, try PySide (with QtGui imported as QtWidgets) 24 | except Exception as e: 25 | import PySide.QtCore as QtCore 26 | import PySide.QtGui as QtGui 27 | import PySide.QtGui as QtWidgets 28 | 29 | 30 | # ignore if in python shell 31 | except Exception as e: 32 | pass 33 | 34 | 35 | 36 | # EMBEDDED UI 37 | 38 | 39 | class Ui_Dialog(object): 40 | def setupUi(self, Dialog): 41 | Dialog.setObjectName("Dialog") 42 | Dialog.setWindowModality(QtCore.Qt.ApplicationModal) 43 | Dialog.setEnabled(True) 44 | Dialog.resize(897, 868) 45 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 46 | sizePolicy.setHorizontalStretch(0) 47 | sizePolicy.setVerticalStretch(0) 48 | sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) 49 | Dialog.setSizePolicy(sizePolicy) 50 | Dialog.setFocusPolicy(QtCore.Qt.StrongFocus) 51 | Dialog.setWindowOpacity(1.0) 52 | Dialog.setAutoFillBackground(False) 53 | Dialog.setWindowFilePath("") 54 | Dialog.setSizeGripEnabled(False) 55 | Dialog.setModal(False) 56 | self.ListCopyPaths = QtWidgets.QListWidget(Dialog) 57 | self.ListCopyPaths.setGeometry(QtCore.QRect(20, 240, 421, 411)) 58 | self.ListCopyPaths.setAutoFillBackground(False) 59 | self.ListCopyPaths.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 60 | self.ListCopyPaths.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 61 | self.ListCopyPaths.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 62 | self.ListCopyPaths.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 63 | self.ListCopyPaths.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 64 | self.ListCopyPaths.setResizeMode(QtWidgets.QListView.Fixed) 65 | self.ListCopyPaths.setObjectName("ListCopyPaths") 66 | self.ListIgnorePaths = QtWidgets.QListWidget(Dialog) 67 | self.ListIgnorePaths.setGeometry(QtCore.QRect(460, 240, 421, 411)) 68 | self.ListIgnorePaths.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 69 | self.ListIgnorePaths.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 70 | self.ListIgnorePaths.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 71 | self.ListIgnorePaths.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 72 | self.ListIgnorePaths.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 73 | self.ListIgnorePaths.setObjectName("ListIgnorePaths") 74 | self.SendToIgnore = QtWidgets.QPushButton(Dialog) 75 | self.SendToIgnore.setGeometry(QtCore.QRect(290, 660, 151, 23)) 76 | self.SendToIgnore.setAutoDefault(False) 77 | self.SendToIgnore.setObjectName("SendToIgnore") 78 | self.SendToCopy = QtWidgets.QPushButton(Dialog) 79 | self.SendToCopy.setGeometry(QtCore.QRect(460, 660, 151, 23)) 80 | self.SendToCopy.setAutoDefault(False) 81 | self.SendToCopy.setObjectName("SendToCopy") 82 | self.PackedPath = QtWidgets.QLineEdit(Dialog) 83 | self.PackedPath.setGeometry(QtCore.QRect(20, 40, 781, 20)) 84 | self.PackedPath.setText("") 85 | self.PackedPath.setObjectName("PackedPath") 86 | self.ChoosePackedPathButton = QtWidgets.QPushButton(Dialog) 87 | self.ChoosePackedPathButton.setGeometry(QtCore.QRect(810, 39, 75, 23)) 88 | self.ChoosePackedPathButton.setAutoDefault(False) 89 | self.ChoosePackedPathButton.setObjectName("ChoosePackedPathButton") 90 | self.LCopy = QtWidgets.QLabel(Dialog) 91 | self.LCopy.setGeometry(QtCore.QRect(30, 220, 47, 13)) 92 | self.LCopy.setObjectName("LCopy") 93 | self.LIgnore = QtWidgets.QLabel(Dialog) 94 | self.LIgnore.setGeometry(QtCore.QRect(470, 220, 47, 13)) 95 | self.LIgnore.setObjectName("LIgnore") 96 | self.LCurrItemItemPath = QtWidgets.QLabel(Dialog) 97 | self.LCurrItemItemPath.setGeometry(QtCore.QRect(20, 720, 131, 16)) 98 | font = QtGui.QFont() 99 | font.setBold(True) 100 | font.setWeight(75) 101 | self.LCurrItemItemPath.setFont(font) 102 | self.LCurrItemItemPath.setObjectName("LCurrItemItemPath") 103 | self.LPackedItemPath = QtWidgets.QLabel(Dialog) 104 | self.LPackedItemPath.setGeometry(QtCore.QRect(20, 740, 131, 16)) 105 | font = QtGui.QFont() 106 | font.setBold(True) 107 | font.setWeight(75) 108 | self.LPackedItemPath.setFont(font) 109 | self.LPackedItemPath.setObjectName("LPackedItemPath") 110 | self.CurrItemPath = QtWidgets.QLabel(Dialog) 111 | self.CurrItemPath.setGeometry(QtCore.QRect(150, 720, 771, 16)) 112 | self.CurrItemPath.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) 113 | self.CurrItemPath.setObjectName("CurrItemPath") 114 | self.PackedItemPath = QtWidgets.QLabel(Dialog) 115 | self.PackedItemPath.setGeometry(QtCore.QRect(150, 740, 771, 16)) 116 | self.PackedItemPath.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) 117 | self.PackedItemPath.setObjectName("PackedItemPath") 118 | self.LWebsite = QtWidgets.QLabel(Dialog) 119 | self.LWebsite.setGeometry(QtCore.QRect(20, 830, 221, 16)) 120 | self.LWebsite.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 121 | self.LWebsite.setOpenExternalLinks(True) 122 | self.LWebsite.setObjectName("LWebsite") 123 | self.LPath = QtWidgets.QLabel(Dialog) 124 | self.LPath.setGeometry(QtCore.QRect(20, 20, 781, 16)) 125 | self.LPath.setObjectName("LPath") 126 | self.LFiles = QtWidgets.QLabel(Dialog) 127 | self.LFiles.setGeometry(QtCore.QRect(20, 760, 131, 16)) 128 | font = QtGui.QFont() 129 | font.setBold(True) 130 | font.setWeight(75) 131 | self.LFiles.setFont(font) 132 | self.LFiles.setObjectName("LFiles") 133 | self.CurrItemFiles = QtWidgets.QLabel(Dialog) 134 | self.CurrItemFiles.setGeometry(QtCore.QRect(150, 760, 771, 16)) 135 | self.CurrItemFiles.setObjectName("CurrItemFiles") 136 | self.TotalProgress = QtWidgets.QProgressBar(Dialog) 137 | self.TotalProgress.setEnabled(True) 138 | self.TotalProgress.setGeometry(QtCore.QRect(370, 830, 161, 23)) 139 | self.TotalProgress.setProperty("value", 0) 140 | self.TotalProgress.setTextVisible(True) 141 | self.TotalProgress.setOrientation(QtCore.Qt.Horizontal) 142 | self.TotalProgress.setInvertedAppearance(False) 143 | self.TotalProgress.setTextDirection(QtWidgets.QProgressBar.TopToBottom) 144 | self.TotalProgress.setObjectName("TotalProgress") 145 | self.Refresh = QtWidgets.QPushButton(Dialog) 146 | self.Refresh.setGeometry(QtCore.QRect(800, 660, 75, 23)) 147 | self.Refresh.setAutoDefault(False) 148 | self.Refresh.setObjectName("Refresh") 149 | self.Start = QtWidgets.QPushButton(Dialog) 150 | self.Start.setGeometry(QtCore.QRect(710, 830, 75, 23)) 151 | self.Start.setAutoDefault(False) 152 | self.Start.setObjectName("Start") 153 | self.Interrupt = QtWidgets.QPushButton(Dialog) 154 | self.Interrupt.setGeometry(QtCore.QRect(800, 830, 75, 23)) 155 | self.Interrupt.setAutoDefault(False) 156 | self.Interrupt.setObjectName("Interrupt") 157 | self.LSize = QtWidgets.QLabel(Dialog) 158 | self.LSize.setGeometry(QtCore.QRect(20, 780, 131, 16)) 159 | font = QtGui.QFont() 160 | font.setBold(True) 161 | font.setWeight(75) 162 | self.LSize.setFont(font) 163 | self.LSize.setObjectName("LSize") 164 | self.CurrItemSize = QtWidgets.QLabel(Dialog) 165 | self.CurrItemSize.setGeometry(QtCore.QRect(150, 780, 771, 16)) 166 | self.CurrItemSize.setObjectName("CurrItemSize") 167 | self.LTotalCopySize = QtWidgets.QLabel(Dialog) 168 | self.LTotalCopySize.setGeometry(QtCore.QRect(560, 830, 71, 21)) 169 | font = QtGui.QFont() 170 | font.setBold(True) 171 | font.setWeight(75) 172 | self.LTotalCopySize.setFont(font) 173 | self.LTotalCopySize.setObjectName("LTotalCopySize") 174 | self.TotalCopySize = QtWidgets.QLabel(Dialog) 175 | self.TotalCopySize.setGeometry(QtCore.QRect(640, 830, 71, 21)) 176 | self.TotalCopySize.setObjectName("TotalCopySize") 177 | self.IgnoredLabel = QtWidgets.QLabel(Dialog) 178 | self.IgnoredLabel.setGeometry(QtCore.QRect(20, 700, 131, 16)) 179 | font = QtGui.QFont() 180 | font.setBold(True) 181 | font.setWeight(75) 182 | self.IgnoredLabel.setFont(font) 183 | self.IgnoredLabel.setToolTip("") 184 | self.IgnoredLabel.setObjectName("IgnoredLabel") 185 | self.CurrentCopyItem = QtWidgets.QLabel(Dialog) 186 | self.CurrentCopyItem.setGeometry(QtCore.QRect(370, 800, 161, 21)) 187 | self.CurrentCopyItem.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) 188 | self.CurrentCopyItem.setObjectName("CurrentCopyItem") 189 | self.GoToFolder = QtWidgets.QPushButton(Dialog) 190 | self.GoToFolder.setEnabled(False) 191 | self.GoToFolder.setGeometry(QtCore.QRect(20, 660, 75, 23)) 192 | self.GoToFolder.setAutoDefault(False) 193 | self.GoToFolder.setObjectName("GoToFolder") 194 | self.ItemProgress = QtWidgets.QProgressBar(Dialog) 195 | self.ItemProgress.setEnabled(True) 196 | self.ItemProgress.setGeometry(QtCore.QRect(370, 800, 161, 23)) 197 | self.ItemProgress.setProperty("value", 0) 198 | self.ItemProgress.setTextVisible(False) 199 | self.ItemProgress.setTextDirection(QtWidgets.QProgressBar.TopToBottom) 200 | self.ItemProgress.setObjectName("ItemProgress") 201 | self.SettingPages = QtWidgets.QTabWidget(Dialog) 202 | self.SettingPages.setGeometry(QtCore.QRect(20, 90, 861, 111)) 203 | self.SettingPages.setObjectName("SettingPages") 204 | self.MainSettings = QtWidgets.QWidget() 205 | self.MainSettings.setObjectName("MainSettings") 206 | self.RelinkPaths = QtWidgets.QCheckBox(self.MainSettings) 207 | self.RelinkPaths.setGeometry(QtCore.QRect(20, 20, 271, 17)) 208 | self.RelinkPaths.setCheckable(True) 209 | self.RelinkPaths.setChecked(True) 210 | self.RelinkPaths.setObjectName("RelinkPaths") 211 | self.RelativeRelink = QtWidgets.QCheckBox(self.MainSettings) 212 | self.RelativeRelink.setEnabled(True) 213 | self.RelativeRelink.setGeometry(QtCore.QRect(20, 40, 321, 17)) 214 | self.RelativeRelink.setChecked(True) 215 | self.RelativeRelink.setObjectName("RelativeRelink") 216 | self.LParentDirectories = QtWidgets.QLabel(self.MainSettings) 217 | self.LParentDirectories.setGeometry(QtCore.QRect(510, 40, 191, 21)) 218 | self.LParentDirectories.setObjectName("LParentDirectories") 219 | self.ParentDirectories = QtWidgets.QSpinBox(self.MainSettings) 220 | self.ParentDirectories.setGeometry(QtCore.QRect(460, 40, 41, 21)) 221 | self.ParentDirectories.setSuffix("") 222 | self.ParentDirectories.setPrefix("") 223 | self.ParentDirectories.setMinimum(1) 224 | self.ParentDirectories.setMaximum(99) 225 | self.ParentDirectories.setProperty("value", 3) 226 | self.ParentDirectories.setObjectName("ParentDirectories") 227 | self.NodeNameFolder = QtWidgets.QCheckBox(self.MainSettings) 228 | self.NodeNameFolder.setGeometry(QtCore.QRect(460, 20, 271, 17)) 229 | self.NodeNameFolder.setChecked(True) 230 | self.NodeNameFolder.setObjectName("NodeNameFolder") 231 | self.SettingPages.addTab(self.MainSettings, "") 232 | self.tab = QtWidgets.QWidget() 233 | self.tab.setObjectName("tab") 234 | self.CopyFontDir = QtWidgets.QCheckBox(self.tab) 235 | self.CopyFontDir.setGeometry(QtCore.QRect(20, 20, 271, 17)) 236 | self.CopyFontDir.setChecked(True) 237 | self.CopyFontDir.setObjectName("CopyFontDir") 238 | self.CopyGizmos = QtWidgets.QCheckBox(self.tab) 239 | self.CopyGizmos.setGeometry(QtCore.QRect(20, 40, 271, 17)) 240 | self.CopyGizmos.setChecked(True) 241 | self.CopyGizmos.setObjectName("CopyGizmos") 242 | self.SettingPages.addTab(self.tab, "") 243 | self.Misc = QtWidgets.QWidget() 244 | self.Misc.setObjectName("Misc") 245 | self.ContinueOnError = QtWidgets.QCheckBox(self.Misc) 246 | self.ContinueOnError.setGeometry(QtCore.QRect(20, 20, 171, 17)) 247 | self.ContinueOnError.setChecked(True) 248 | self.ContinueOnError.setTristate(False) 249 | self.ContinueOnError.setObjectName("ContinueOnError") 250 | self.ExitOnFinish = QtWidgets.QCheckBox(self.Misc) 251 | self.ExitOnFinish.setGeometry(QtCore.QRect(20, 40, 171, 17)) 252 | self.ExitOnFinish.setChecked(False) 253 | self.ExitOnFinish.setTristate(False) 254 | self.ExitOnFinish.setObjectName("ExitOnFinish") 255 | self.CSVSeparator = QtWidgets.QComboBox(self.Misc) 256 | self.CSVSeparator.setGeometry(QtCore.QRect(460, 18, 41, 22)) 257 | self.CSVSeparator.setObjectName("CSVSeparator") 258 | self.CSVSeparator.addItem("") 259 | self.CSVSeparator.addItem("") 260 | self.LCSVSeparator = QtWidgets.QLabel(self.Misc) 261 | self.LCSVSeparator.setGeometry(QtCore.QRect(510, 18, 191, 21)) 262 | self.LCSVSeparator.setObjectName("LCSVSeparator") 263 | self.License = QtWidgets.QComboBox(self.Misc) 264 | self.License.setGeometry(QtCore.QRect(460, 38, 41, 22)) 265 | self.License.setObjectName("License") 266 | self.License.addItem("") 267 | self.License.addItem("") 268 | self.LLicense = QtWidgets.QLabel(self.Misc) 269 | self.LLicense.setGeometry(QtCore.QRect(510, 38, 191, 21)) 270 | self.LLicense.setObjectName("LLicense") 271 | self.SettingPages.addTab(self.Misc, "") 272 | self.GoToRootFolder = QtWidgets.QPushButton(Dialog) 273 | self.GoToRootFolder.setGeometry(QtCore.QRect(810, 70, 75, 23)) 274 | self.GoToRootFolder.setAutoDefault(False) 275 | self.GoToRootFolder.setObjectName("GoToRootFolder") 276 | self.GoToNode = QtWidgets.QPushButton(Dialog) 277 | self.GoToNode.setEnabled(False) 278 | self.GoToNode.setGeometry(QtCore.QRect(100, 660, 75, 23)) 279 | self.GoToNode.setAutoDefault(False) 280 | self.GoToNode.setObjectName("GoToNode") 281 | self.ItemProgress.raise_() 282 | self.ListCopyPaths.raise_() 283 | self.ListIgnorePaths.raise_() 284 | self.SendToIgnore.raise_() 285 | self.SendToCopy.raise_() 286 | self.PackedPath.raise_() 287 | self.ChoosePackedPathButton.raise_() 288 | self.LCopy.raise_() 289 | self.LIgnore.raise_() 290 | self.LCurrItemItemPath.raise_() 291 | self.LPackedItemPath.raise_() 292 | self.CurrItemPath.raise_() 293 | self.PackedItemPath.raise_() 294 | self.LWebsite.raise_() 295 | self.LPath.raise_() 296 | self.LFiles.raise_() 297 | self.CurrItemFiles.raise_() 298 | self.TotalProgress.raise_() 299 | self.Refresh.raise_() 300 | self.Start.raise_() 301 | self.Interrupt.raise_() 302 | self.LSize.raise_() 303 | self.CurrItemSize.raise_() 304 | self.LTotalCopySize.raise_() 305 | self.TotalCopySize.raise_() 306 | self.IgnoredLabel.raise_() 307 | self.CurrentCopyItem.raise_() 308 | self.GoToFolder.raise_() 309 | self.SettingPages.raise_() 310 | self.GoToRootFolder.raise_() 311 | self.GoToNode.raise_() 312 | 313 | self.retranslateUi(Dialog) 314 | self.SettingPages.setCurrentIndex(0) 315 | self.CSVSeparator.setCurrentIndex(0) 316 | self.License.setCurrentIndex(0) 317 | QtCore.QMetaObject.connectSlotsByName(Dialog) 318 | 319 | def retranslateUi(self, Dialog): 320 | _translate = QtCore.QCoreApplication.translate 321 | Dialog.setWindowTitle(_translate("Dialog", "Nuke打包工具 [WrapItUp - 1.8] 作者- Max van Leeuwen SkydreamVFX汉化")) 322 | self.ListCopyPaths.setToolTip(_translate("Dialog", "

在左侧框内所有文件都复制打包到指定的路径.

")) 323 | self.ListIgnorePaths.setToolTip(_translate("Dialog", "

在右侧框内所有文件都不会复制打包.

")) 324 | self.SendToIgnore.setToolTip(_translate("Dialog", "选中并按下此按钮会将左侧所选的项目移至右侧,并在打包时忽略.")) 325 | self.SendToIgnore.setText(_translate("Dialog", ">>")) 326 | self.SendToCopy.setToolTip(_translate("Dialog", "选中并按下此按钮会将右侧所选的项目移至左侧,并在打包时打包这些文件及项目.")) 327 | self.SendToCopy.setText(_translate("Dialog", "<<")) 328 | self.PackedPath.setToolTip(_translate("Dialog", "

所有数据收集到的文件夹的位置.
确保这是个空文件夹.

")) 329 | self.ChoosePackedPathButton.setToolTip(_translate("Dialog", "

所有数据收集到的文件夹的位置.
确保这是个空文件夹.

")) 330 | self.ChoosePackedPathButton.setText(_translate("Dialog", "选择文件夹")) 331 | self.LCopy.setText(_translate("Dialog", "打包")) 332 | self.LIgnore.setText(_translate("Dialog", "忽略")) 333 | self.LCurrItemItemPath.setToolTip(_translate("Dialog", "所选文件的路径.")) 334 | self.LCurrItemItemPath.setText(_translate("Dialog", "所选文件的路径")) 335 | self.LPackedItemPath.setToolTip(_translate("Dialog", "

打包完成后的路径预览.

")) 336 | self.LPackedItemPath.setText(_translate("Dialog", "打包完成后的路径预览")) 337 | self.CurrItemPath.setText(_translate("Dialog", "-")) 338 | self.PackedItemPath.setText(_translate("Dialog", "-")) 339 | self.LWebsite.setToolTip(_translate("Dialog", "作者网站!")) 340 | self.LWebsite.setText(_translate("Dialog", "

maxvanleeuwen.com/WrapItUp

")) 341 | self.LPath.setToolTip(_translate("Dialog", "

要将所有数据收集到的文件夹的位置.
确保这是一个空文件夹.

")) 342 | self.LPath.setText(_translate("Dialog", "选择要保存的文件夹:")) 343 | self.LFiles.setToolTip(_translate("Dialog", "

当前所选文件的 个数(单文件)/帧数(序列文件).

")) 344 | self.LFiles.setText(_translate("Dialog", "当前所选的文件数")) 345 | self.CurrItemFiles.setText(_translate("Dialog", "-")) 346 | self.TotalProgress.setToolTip(_translate("Dialog", "

打包进度.

")) 347 | self.Refresh.setToolTip(_translate("Dialog", "

重新检查当前的Nuke脚本内的所有素材和其他文件.

")) 348 | self.Refresh.setText(_translate("Dialog", "刷新")) 349 | self.Start.setToolTip(_translate("Dialog", "

点击开始打包.
完成后会打开文件夹.

")) 350 | self.Start.setText(_translate("Dialog", "开始打包...")) 351 | self.Interrupt.setToolTip(_translate("Dialog", "

Stop/退出.

")) 352 | self.Interrupt.setText(_translate("Dialog", "停止打包")) 353 | self.LSize.setToolTip(_translate("Dialog", "

所选文件的大小.

")) 354 | self.LSize.setText(_translate("Dialog", "当前文件占用空间")) 355 | self.CurrItemSize.setText(_translate("Dialog", "-")) 356 | self.LTotalCopySize.setToolTip(_translate("Dialog", "要打包的所有文件的总占用空间.")) 357 | self.LTotalCopySize.setText(_translate("Dialog", "总占用空间")) 358 | self.TotalCopySize.setToolTip(_translate("Dialog", "要打包的所有文件的总占用空间")) 359 | self.TotalCopySize.setText(_translate("Dialog", "0")) 360 | self.IgnoredLabel.setText(_translate("Dialog", "已被忽略!")) 361 | self.CurrentCopyItem.setText(_translate("Dialog", "正在加载...")) 362 | self.GoToFolder.setToolTip(_translate("Dialog", "

打开当前所选文件所在的文件夹.

alt/option+shift+r

")) 363 | self.GoToFolder.setText(_translate("Dialog", "定位到文件夹")) 364 | self.GoToFolder.setShortcut(_translate("Dialog", "Alt+Shift+R")) 365 | self.ItemProgress.setToolTip(_translate("Dialog", "

进度.

")) 366 | self.RelinkPaths.setToolTip(_translate("Dialog", "

创建一个工程文件副本,在这个副本中,所有素材路径会重新填写为打包位置的素材路径.

")) 367 | self.RelinkPaths.setText(_translate("Dialog", "重新链接所有素材到打包位置")) 368 | self.RelativeRelink.setToolTip(_translate("Dialog", "

创建一个工程文件副本,在这个副本中,所有文件路径用以下路径前缀重新链接: [python {nuke.script_directory()}]

这样做的好处是整个文件夹换到别的位置或者在别的机器上打开仍然不会报路径错误,因为素材读取的位置是相对工程文件脚本的,确保工程文件所在的文件夹里“media”这个文件夹不改变和工程文件的相对位置,工程文件打开就永远不会报找不到素材路径的错误.

")) 369 | self.RelativeRelink.setText(_translate("Dialog", "重新链接所有素材为相对路径")) 370 | self.LParentDirectories.setToolTip(_translate("Dialog", "

找到的素材所在路径里要保留的父目录的层级数量.

举个栗子:
现在有个素材文件 /path/to/image/files/file_####.exr 如果数值设为3,则在打包完成后打包所在的位置会有如下文件:

../MEDIA/image/files/file_####.exr


如果没有勾选 \'打包素材放入带有原始文件路径的父目录文件夹\' 也就是没有选中, 那么这个数值不要设置的太小.
如果存在相同名称父目录的素材文件可能会被覆盖或合并在一起.
建议设置为3

")) 371 | self.LParentDirectories.setText(_translate("Dialog", "要保留的文件父目录层级数")) 372 | self.ParentDirectories.setToolTip(_translate("Dialog", "

找到的素材所在路径里要保留的父目录的层级数量.

For instance:
举个栗子:
现在有个素材文件 /path/to/image/files/file_####.exr 如果数值设为3,则在打包完成后打包所在的位置会有如下文件:

../MEDIA/image/files/file_####.exr


如果没有勾选 \'打包素材放入带有原始文件路径的父目录文件夹\' 也就是没有选中, 那么这个数值不要设置的太小.
如果存在相同名称父目录的素材文件可能会被覆盖或合并在一起.

")) 373 | self.NodeNameFolder.setToolTip(_translate("Dialog", "

将每一个素材分别放在一个文件夹里,这个Read节点的名字会设置为文件夹的名字。.

这样有助于查找哪个素材属于新工程中的哪个节点,并且是针对不同素材项的父文件夹具有相同名称时可能出现的这类问题的一个查询办法,当文件过多时也不容易混淆.

")) 374 | self.NodeNameFolder.setText(_translate("Dialog", "打包素材放入带有原始文件路径的父目录文件夹")) 375 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.MainSettings), _translate("Dialog", "素材")) 376 | self.CopyFontDir.setToolTip(_translate("Dialog", "

如果目前工程里面设置了自定义的字体路径,那么就顺带一起打包.

")) 377 | self.CopyFontDir.setText(_translate("Dialog", "打包自定义字体文件(如果有)")) 378 | self.CopyGizmos.setToolTip(_translate("Dialog", "

如果Nuke脚本中使用了gzimo插件,会自动收集并生成init.py和menu.py文件,在别的电脑上安装会变得更轻松,但如果在别的电脑上打开而且他们没有安装这些gizmo,打开工程仍然会报错。

这个功能不适用于所有插件,
例如不适用后缀名为(.dll,.so,.dylib)的插件.

")) 379 | self.CopyGizmos.setText(_translate("Dialog", "打包 gizmos")) 380 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.tab), _translate("Dialog", "附加组件")) 381 | self.ContinueOnError.setToolTip(_translate("Dialog", "

如果发生错误仍会继续复制打包。
如果存在错误,请检查所选目录文件夹生成的log.txt文件.

")) 382 | self.ContinueOnError.setText(_translate("Dialog", "如果发生错误仍会继续复制打包")) 383 | self.ExitOnFinish.setToolTip(_translate("Dialog", "

打包完成时(或出错后,如果禁用了上面“出错也继续打包”,则完全关闭Nuke)。

对于在许可证上使用了有限数量的浮动许可证的电脑有点用.

")) 384 | self.ExitOnFinish.setText(_translate("Dialog", "打包完成后关闭Nuke")) 385 | self.CSVSeparator.setToolTip(_translate("Dialog", "

设置日志文件的CSV列分隔符.

")) 386 | self.CSVSeparator.setItemText(0, _translate("Dialog", ";")) 387 | self.CSVSeparator.setItemText(1, _translate("Dialog", ",")) 388 | self.LCSVSeparator.setToolTip(_translate("Dialog", "

设置日志文件的CSV列分隔符.

")) 389 | self.LCSVSeparator.setText(_translate("Dialog", "日志文件的 CSV 分隔符 选项")) 390 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.Misc), _translate("Dialog", "其他")) 391 | self.License.setToolTip(_translate("Dialog", "

在命令行中运行Nuke时(重新链接时),在内部使用此标志。
-t标志使用nuke_r许可证,-ti标志使用nuke_i许可证。

")) 392 | self.License.setItemText(0, _translate("Dialog", "-t")) 393 | self.License.setItemText(1, _translate("Dialog", "-ti")) 394 | self.LLicense.setToolTip(_translate("Dialog", "

在命令行中运行Nuke时(重新链接时)在内部使用此标志。
-t标志使用nuke_r许可证,-ti标志使用nuke_i许可证.

")) 395 | self.LLicense.setText(_translate("Dialog", "重新链接 license")) 396 | self.GoToRootFolder.setToolTip(_translate("Dialog", "

打开当前选择的文件夹.

")) 397 | self.GoToRootFolder.setText(_translate("Dialog", "打开文件夹")) 398 | self.GoToNode.setToolTip(_translate("Dialog", "

打开当前所选文件所在的文件夹.

alt/option+shift+r

")) 399 | self.GoToNode.setText(_translate("Dialog", "定位到节点")) 400 | 401 | 402 | # END OF EMBEDDED UI 403 | 404 | 405 | 406 | # import necessary 407 | import sys 408 | import nuke 409 | import shutil 410 | import threading 411 | import os 412 | import glob 413 | import re 414 | import subprocess 415 | import webbrowser 416 | import time 417 | 418 | 419 | 420 | # globals 421 | WIU_PackedPath = '' 422 | WIU_Interrupted = False 423 | WIU_TotalSize = 0 424 | WIU_Copying = False 425 | WIU_ProjectDir = False 426 | 427 | WIU_Relink = True 428 | WIU_RelinkRelative = True 429 | WIU_Gizmo = True 430 | WIU_Fonts = True 431 | WIU_ParentDirCount = 3 432 | WIU_NodeNameFolder = True 433 | WIU_AppPath = '' 434 | 435 | WIU_SilentReturn = [] 436 | WIU_SilentList = [] 437 | 438 | WIU_MediaDataNotIgnored = [] 439 | 440 | 441 | 442 | # bytes to readable string 443 | def BytesToString(B): 444 | B = float(B) 445 | KB = float(1024) 446 | MB = float(KB ** 2) # 1.048.576 447 | GB = float(KB ** 3) # 1.073.741.824 448 | TB = float(KB ** 4) # 1.099.511.627.776 449 | 450 | if B < KB: 451 | return '{0} {1}'.format(B, 'B') 452 | elif KB <= B < MB: 453 | return '{0:.2f} KB'.format(B/KB) 454 | elif MB <= B < GB: 455 | return '{0:.2f} MB'.format(B/MB) 456 | elif GB <= B < TB: 457 | return '{0:.2f} GB'.format(B/GB) 458 | elif TB <= B: 459 | return '{0:.2f} TB'.format(B/TB) 460 | 461 | 462 | 463 | def evalTCL(text): 464 | 465 | val = '' 466 | try: 467 | val = nuke.tcl("[return \"" + text + "\"]") 468 | except Exception as e: 469 | # TCL not working for this string 470 | pass 471 | 472 | # only allow string type to be returned 473 | if type(val) is not str: 474 | val = text 475 | 476 | return val 477 | 478 | 479 | 480 | 481 | def _Start(silent = False, nk = '', startnow = False, out = '', nodenamefolder = True, parentdircount = 3, relinked = True, relativerelinked = True, media = True, skipdisablednodes = False, fonts = True, gizmos = True, csvcommas = False, licinteractive = False): 482 | 483 | # reset list 484 | global WIU_SilentList 485 | WIU_SilentList = [] 486 | 487 | 488 | # add project directory to start of path (or get project dir path) 489 | def ProjectDirectory(pth = ''): 490 | 491 | # set global projectdir 492 | global WIU_ProjectDir 493 | WIU_ProjectDir = True 494 | 495 | # get project directory 496 | projectdir = nuke.root()['project_directory'].getValue() 497 | 498 | # remove double slash 499 | if projectdir.endswith('/') and pth.startswith('/'): 500 | projectdir = projectdir[:-1] 501 | 502 | # stitch together 503 | pth = projectdir + pth 504 | 505 | return pth 506 | 507 | 508 | # returns all paths one file knob could refer to 509 | def GetRealKnobPaths(knobPath): 510 | 511 | # container for all found paths in sequence 512 | paths = [] 513 | 514 | # all versions of knobPath (stereo views, %v) 515 | viewFiles = [] 516 | 517 | # project directory relative path 518 | projectdir = False 519 | 520 | # check if stereo files are used 521 | if r'%v' in knobPath or r'%V' in knobPath: 522 | 523 | # get all stereo views in the comp 524 | viewRtn = nuke.root().knob('views').toScript() 525 | 526 | for rtn in viewRtn.split('\n'): 527 | 528 | # get each view name in the nuke comp 529 | view = rtn.split(' ')[-2] 530 | 531 | # replace in path and append to viewfiles 532 | viewFiles.append(knobPath.replace(r'%v', view).replace(r'%V', view)) 533 | 534 | # if not, do not replace anything 535 | else: 536 | viewFiles = [knobPath] 537 | 538 | 539 | # overwrite knobPath value with new value per view 540 | for knobPath in viewFiles: 541 | 542 | # get TCL evaluated string 543 | knobPathTCL = evalTCL(knobPath) 544 | 545 | # get parent directory 546 | knobPathParentDir = os.path.dirname(knobPathTCL) 547 | 548 | 549 | # try appending project root folder, if the dir does not exist 550 | if not os.path.exists(knobPathParentDir): 551 | 552 | knobPath_projectdir = ProjectDirectory(knobPathParentDir) 553 | 554 | if os.path.isdir(knobPath_projectdir): 555 | projectdir = True 556 | knobPathParentDir = knobPath_projectdir 557 | knobPathTCL = ProjectDirectory(knobPathTCL) 558 | 559 | 560 | 561 | 562 | # check if the parent dir exists 563 | if os.path.exists(knobPathParentDir): 564 | 565 | # if it does, get the filename 566 | filename = knobPathTCL.split('/')[-1] 567 | 568 | # get number from printf notation as int 569 | printfCount = -1 570 | try: 571 | # regex pattern for printf notation (only get first result of found printf notations) 572 | regex = re.compile('%..d') 573 | regexFile = regex.findall(filename)[0] 574 | printfCount = int(regexFile[1:-1]) 575 | except Exception as e: 576 | # no printf used 577 | pass 578 | 579 | 580 | # if printf notation is used for the sequence 581 | if printfCount > 0: 582 | 583 | # make wildcard string (e.g. '????') for glob with same length as # 584 | wildcards = '' 585 | for i in range(printfCount): 586 | wildcards += '?' 587 | 588 | wildcardPath = knobPathTCL.replace(regexFile, wildcards) 589 | 590 | # get all files in directory 591 | files = glob.glob(wildcardPath) 592 | for eachFile in files: 593 | paths.append(eachFile.replace('\\', '/')) 594 | 595 | 596 | # if hash notation is used for the sequence 597 | elif '#' in filename: 598 | 599 | # split by # 600 | filenameSplit = filename.split('#') 601 | 602 | # count amount of # 603 | wildcardCount = len(filenameSplit) - 2 604 | 605 | # make wildcard string (e.g. '????') for glob with same length as # 606 | wildcards = '' 607 | for i in range(wildcardCount + 1): 608 | wildcards += '?' 609 | 610 | # get full filename with wildcard replaced 611 | filename = filenameSplit[ -len(filenameSplit) ] + wildcards + filenameSplit[ -1 ] 612 | 613 | # full file path 614 | wildcardPath = os.path.join(knobPathParentDir, filename).replace('\\', '/') 615 | 616 | # get all files that match wildcard pattern 617 | files = glob.glob(wildcardPath) 618 | for eachFile in files: 619 | paths.append(eachFile.replace('\\', '/')) 620 | 621 | 622 | # if not a sequence 623 | else: 624 | 625 | # append this file to paths, if it exists 626 | if os.path.isfile(knobPathTCL): 627 | paths.append(knobPathTCL) 628 | 629 | # check if it is a relative (project directory) path 630 | elif os.path.isfile(ProjectDirectory(knobPathTCL)): 631 | paths.append(ProjectDirectory(knobPathTCL)) 632 | 633 | 634 | # return result 635 | return [paths, projectdir] 636 | 637 | 638 | 639 | # function to shorten a path to where it can be appended to the chosen packed media directory path 640 | def PackedPath(fullPath, i = 0, nodeName = '', fontFolder = '', project_dir = False, settingFontPath = False): 641 | 642 | global WIU_NodeNameFolder 643 | global WIU_ParentDirCount 644 | 645 | # set node name folder to argument if the UI is not loaded 646 | if silent: 647 | WIU_NodeNameFolder = nodenamefolder 648 | WIU_ParentDirCount = parentdircount 649 | 650 | # TCL eval 651 | fullPath = evalTCL(fullPath) 652 | 653 | 654 | # media (default) 655 | if i == 0: 656 | 657 | # get amount of parent directories 658 | parentDirCount = WIU_ParentDirCount 659 | 660 | # split into parent dirs 661 | splitPath = fullPath.split('/') 662 | splitCleanPath = [] 663 | for s in splitPath: 664 | if s is not '': 665 | splitCleanPath.append(s) 666 | 667 | 668 | # build shortened path (do not add / at the end of the path) 669 | newPath = (nodeName + '/') if (WIU_NodeNameFolder and not project_dir) else '' 670 | 671 | # make actual parent dir count equal to user-chosen parent dir count when it is higher, and when the current media is from project_dir 672 | if parentDirCount > len(splitCleanPath) or project_dir: 673 | parentDirCount = len(splitCleanPath) 674 | 675 | for c in range(parentDirCount): 676 | newPath += splitCleanPath[ len(splitCleanPath) - (parentDirCount - c) ] + ('/' if not c == parentDirCount - 1 else '') 677 | 678 | if project_dir: 679 | newPath = newPath.replace(ProjectDirectory(), '') 680 | 681 | 682 | # sanitise string 683 | illegalChars = ':<>$?!;\'\"\\`*|' 684 | for c in illegalChars: 685 | newPath = newPath.replace(c, '_') 686 | 687 | 688 | subdir = 'MEDIA' 689 | if project_dir: 690 | subdir = 'PROJECT_DIRECTORY' 691 | 692 | newPath = WIU_PackedPath + '/' + subdir + '/' + newPath 693 | 694 | return newPath 695 | 696 | fileName = fullPath.split('/')[-1] 697 | 698 | # nuke script 699 | if i == -1: 700 | 701 | newPath = WIU_PackedPath + '/' + fullPath.split('/')[-1] 702 | return newPath 703 | 704 | # nuke script relinked 705 | if i == -2: 706 | 707 | newPath = WIU_PackedPath + '/' + os.path.splitext( fileName )[0] + '_RELINKED.nk' 708 | return newPath 709 | 710 | # nuke script relinked relative 711 | if i == -3: 712 | 713 | newPath = WIU_PackedPath + '/' + os.path.splitext( fileName )[0] + '_RELINKED-RELATIVE.nk' 714 | return newPath 715 | 716 | # font directory 717 | if i == -4: 718 | 719 | newPath = WIU_PackedPath + '/' + 'FONTS' + '/' + fontFolder.split('/')[-1] + ( ('/' + fullPath.split('/')[-1]) if not settingFontPath else '') 720 | return newPath 721 | 722 | # gizmo 723 | if i <= -5: 724 | 725 | newPath = WIU_PackedPath + '/' + 'GIZMOS/Collected' + '/' + os.path.splitext( fileName )[0] + '/' + fileName 726 | return newPath 727 | 728 | 729 | 730 | # gets all file knobs in the comp file and runs them through GetRealKnobPaths() - returns results 731 | def ReadCompMediaData(): 732 | 733 | # progress bar total value 734 | prTotal = len(nuke.allNodes(recurseGroups=True)) 735 | 736 | # container for all loaded files 737 | readFiles = [] 738 | 739 | # collect all knobs with files in them 740 | iNode = 0 741 | for eachNode in nuke.allNodes(recurseGroups=True): 742 | if(silent and eachNode.knob('disable')): # only if a 'disable' knob exists on this node 743 | if(skipdisablednodes and eachNode['disable'].getValue()): continue # if node is disabled and 'ignore disabled nodes' flag is set (terminal only), skip this node 744 | 745 | for eachKnob in eachNode.knobs(): 746 | currKnob = eachNode[eachKnob] 747 | 748 | if currKnob.Class() == 'File_Knob': 749 | 750 | # only add if a path has been entered 751 | foundPath = currKnob.getValue() 752 | if foundPath is not '': 753 | 754 | # get real paths (file path list + project dir bool) 755 | RealKnobPathsResult = GetRealKnobPaths(foundPath) 756 | 757 | realKnobPaths = RealKnobPathsResult[0] 758 | projectdir = RealKnobPathsResult[1] 759 | 760 | # make new list for new paths with their per-file size included 761 | allFilesWithSizes = [] 762 | 763 | # get total file size 764 | totalSize = 0 765 | for eachFile in realKnobPaths: 766 | size = os.path.getsize(eachFile) 767 | totalSize += size 768 | allFilesWithSizes.append([eachFile, size]) 769 | 770 | # check if the path exists 771 | exists = False 772 | if len(realKnobPaths) > 0: 773 | exists = True 774 | 775 | # check if this node is disabled 776 | allNodesDisabled = False 777 | if(eachNode.knob('disable')): 778 | if(eachNode['disable'].getValue()): allNodesDisabled = True 779 | 780 | # make media item (node(s), knob(s), file exists, knob value, all paths and sizes per item, total size, is relative from project directory, bool if all nodes with this media are disabled) 781 | mediaItem = [[eachNode], [eachKnob], exists, foundPath, allFilesWithSizes, totalSize, projectdir, allNodesDisabled] 782 | 783 | # check if the media item has already been found via another node 784 | existingItem = None 785 | i = 0 786 | for m in readFiles: 787 | 788 | # remember item index 789 | if m[4] == mediaItem[4] and m[3] == mediaItem[3]: 790 | existingItem = i 791 | break 792 | i += 1 793 | 794 | # append node and knob to existing item instead of appending the new item 795 | if existingItem is not None: 796 | readFiles[existingItem][0].append(eachNode) 797 | readFiles[existingItem][1].append(eachKnob) 798 | if(readFiles[existingItem][7] and not allNodesDisabled): readFiles[existingItem][7] = False # if previous was disabled and this one isn't, un-disable the node 799 | 800 | # item is new 801 | else: 802 | # append all data to final list 803 | readFiles.append( mediaItem ) 804 | 805 | if not silent: 806 | nuke.executeInMainThread(TotalProgressUpdate, args=(int(round( float(iNode) / float(prTotal) * 100 / 2 )))) 807 | 808 | iNode += 1 809 | 810 | # return results 811 | return readFiles 812 | 813 | 814 | # check if a node is a gizmo, and if so, return the full name 815 | def isGizmo(n): 816 | 817 | # compare type 818 | gizmo = type(n) == nuke.Gizmo 819 | 820 | # append .gizmo 821 | gizmoName = n.Class() if n.Class().endswith('.gizmo') else n.Class() + '.gizmo' 822 | 823 | # return 824 | if gizmo: 825 | return gizmoName 826 | else: 827 | return '' 828 | 829 | 830 | 831 | # window to show when nuke should be saved first 832 | def SaveNukeFirst(): 833 | 834 | nuke.message(u"先保存Nuke脚本!") 835 | window.close() 836 | 837 | 838 | 839 | def ReadCompOtherData(): 840 | 841 | # total progress bar value 842 | prTotal = len(nuke.allNodes(recurseGroups=True)) 843 | 844 | # all non-media data is collected here 845 | readData = [] 846 | 847 | # get current nuke script location 848 | NukeScript = '' 849 | NukeScriptSize = 0 850 | 851 | 852 | if not silent: 853 | try: 854 | NukeScript = nuke.root().name() 855 | NukeScriptSize = os.path.getsize(NukeScript) 856 | 857 | except Exception as e: 858 | nuke.executeInMainThread(SaveNukeFirst, args=()) 859 | return False 860 | else: 861 | NukeScript = nk 862 | NukeScriptSize = os.path.getsize(nk) 863 | 864 | 865 | # item 0: nukescript 866 | readData.append([NukeScript, NukeScriptSize]) 867 | 868 | # item 1: nukescript relinked 869 | readData.append([NukeScript, NukeScriptSize]) 870 | 871 | # item 2: nukescript relative relinked 872 | readData.append([NukeScript, NukeScriptSize]) 873 | 874 | # item 3: get the project font path 875 | fontPathRaw = nuke.root()['free_type_font_path'].getValue() 876 | fontPath = evalTCL(fontPathRaw) 877 | 878 | totalSize = 0 879 | 880 | # collection of all font files 881 | fontPathsS = [] 882 | for path, subdirs, files in os.walk(fontPath): 883 | for eachFile in files: 884 | currFile = os.path.join(path, eachFile).replace('\\', '/') 885 | currSize = os.path.getsize(currFile) 886 | totalSize += currSize 887 | fontPathsS.append([currFile, currSize]) 888 | 889 | # list with font path, total size of all files, list of font files + sizes 890 | readData.append([fontPath, totalSize, fontPathsS]) 891 | 892 | 893 | # item 4: get all gizmos 894 | gizmoList = [] 895 | 896 | # get each gizmo name as string 897 | for eachNode in nuke.allNodes(recurseGroups=True): 898 | 899 | # check if gizmo 900 | gizmoName = isGizmo(eachNode) 901 | 902 | # find gizmo items 903 | if gizmoName is not '': 904 | gizmoItem = [gizmoName, [eachNode]] 905 | 906 | # check if the gizmo was already found earlier, and if so, append only the node to the already found gizmo item 907 | i = 0 908 | alreadyFound = False 909 | for g in gizmoList: 910 | if g[0] == gizmoName: 911 | alreadyFound = True 912 | gizmoList[i][1].append(eachNode) 913 | i+=1 914 | 915 | 916 | if not alreadyFound: 917 | gizmoList.append( gizmoItem ) 918 | 919 | 920 | # iterate through plugin paths to search for them 921 | iNode = 0 922 | for eachPluginPath in nuke.pluginPath(): 923 | 924 | nodeI = 0 925 | for eachGizmo in gizmoList: 926 | gizmoPath = os.path.join(eachPluginPath, eachGizmo[0]).replace('\\', '/') 927 | 928 | if os.path.isfile(gizmoPath): 929 | 930 | # list item: path to gizmo file, size of gizmo file, list of all nodes using the gizmo 931 | readData.append( [gizmoPath, os.path.getsize(gizmoPath), eachGizmo[1]] ) 932 | 933 | nodeI += 1 934 | 935 | if not silent: 936 | nuke.executeInMainThread(TotalProgressUpdate, args=(int(float(iNode) / float(prTotal) * 100) + 50)) 937 | 938 | iNode += 1 939 | 940 | return readData 941 | 942 | 943 | 944 | # function that copies files on all platforms, and creates intermediate directories if needed 945 | # it has some file-specific actions (writing the custom init.py file for copied gizmos, and relinking the nuke scripts) 946 | def ProcessFile(fileFrom, fileTo, writeInit, firstInit, relinkMethod, relinkFonts): 947 | 948 | global WIU_MediaDataNotIgnored 949 | 950 | # caught errors to return 951 | err = '' 952 | 953 | try: 954 | 955 | # create intermediate folders 956 | ToParentFolder = os.path.dirname(fileTo) 957 | if not os.path.isdir(ToParentFolder): 958 | os.makedirs( ToParentFolder ) 959 | 960 | # copy file 961 | shutil.copy2(fileFrom, fileTo) 962 | 963 | 964 | # update init.py/menu.py file 965 | if writeInit: 966 | 967 | # write menu.py/init.py file - file path, is in root path (bool) 968 | def WriteFile(fileName, root): 969 | 970 | # which file 971 | filePath = WIU_PackedPath + '/GIZMOS/' + ('Collected/' if not root else '') + fileName + '.py' 972 | 973 | # try to remove it if logging just started 974 | if firstInit: 975 | try: 976 | os.remove(filePath) 977 | except Exception as e: 978 | pass 979 | 980 | # open file 981 | f = open(filePath, "a+") 982 | 983 | strText = '' 984 | if firstInit: 985 | strText += '# Generated by ' + WIU_Title 986 | 987 | if root: 988 | strText += '\n#\n# Place all contents of the /GIZMOS folder in your /user/.nuke directory to install the necessary gizmos for the collected Nuke script.\n# If an init.py file already exists in /users/.nuke, simply append the following line to that file (instead of overwriting it with this one):\n\n' 989 | strText += "\nnuke.pluginAddPath(\'./Collected\')" 990 | 991 | else: 992 | strText += '\n\n' 993 | 994 | if not root: 995 | 996 | if fileName == 'init': 997 | 998 | gizmoFolderName = fileTo.split('/')[-2] 999 | strText += '\nnuke.pluginAddPath(\"./' + gizmoFolderName + "\")" 1000 | 1001 | elif fileName == 'menu': 1002 | 1003 | gizmoFolderName = fileTo.split('/')[-2] 1004 | strText += "\nnuke.toolbar(\"Nodes\").addCommand(\"Collected/" + gizmoFolderName + "\", \"nuke.createNode('" + gizmoFolderName + ".gizmo')\")" 1005 | 1006 | # write and close 1007 | f.write(strText) 1008 | f.close() 1009 | 1010 | 1011 | # write GIZMOS/init.py 1012 | if firstInit: 1013 | WriteFile('init', True) 1014 | 1015 | # GIZMOS/Collected/init.py 1016 | WriteFile('init', False) 1017 | 1018 | #GIZMOS/Collected/menu.py 1019 | WriteFile('menu', False) 1020 | 1021 | 1022 | # relink nuke script 1023 | if relinkMethod is not -1: 1024 | 1025 | if silent: 1026 | print('\n' + WIU_Log + 'Opening temporary Nuke comp in terminal to relink' + (' (relative)' if relinkMethod == 1 else '')) 1027 | 1028 | # quotation mark 1029 | q = '\"' 1030 | 1031 | # temp terminal file 1032 | filePath = WIU_PackedPath + '/WrapItUp_Temp-RELINK_' + str(relinkMethod) + '.py' 1033 | 1034 | # get nk path 1035 | scriptpath = PackedPath(WIU_OtherData[0][0], i= -2 - relinkMethod) 1036 | 1037 | 1038 | # prepare list of things to do (node name, knob name, new value) 1039 | vList = [] 1040 | 1041 | # fonts (if needed) 1042 | if relinkFonts and fonts: 1043 | vList.append([ 'root', 'free_type_font_path', PackedPath(WIU_OtherData[3][0], i=-4, fontFolder=WIU_OtherData[3][0], settingFontPath=True) ]) #font path 1044 | 1045 | # project directory (if needed) 1046 | if WIU_ProjectDir: 1047 | vList.append([ 'root', 'project_directory', PackedPath('', project_dir = True) ]) #new project directory 1048 | 1049 | # for all media 1050 | if media: 1051 | for d in WIU_MediaData: 1052 | # all knobs should be relinked 1053 | n = 0 1054 | for eachNode in d[0]: 1055 | # do not relink files that aren't copied, or when in project directory 1056 | if (silent or (d in WIU_MediaDataNotIgnored)) and not d[6]: 1057 | vList.append([eachNode.fullName(), d[1][n], PackedPath( d[3], nodeName=getNodeNames(d[0], us=True), project_dir = d[6] )]) 1058 | n += 1 1059 | 1060 | 1061 | # write to pyText 1062 | pyText = '# Generated by ' + WIU_Title + '\n#\n# This is a temporary file used to relink nuke scripts. It can be removed.\n\n' 1063 | pyText += 'nuke.scriptOpen(' + q + scriptpath + q + ')\n' 1064 | 1065 | for v in vList: 1066 | 1067 | # replace path for relinked relative 1068 | if relinkMethod == 1: 1069 | v[2] = v[2].replace(os.path.dirname(scriptpath), '[python {nuke.script_directory()}]') 1070 | 1071 | pyText += '\n' 1072 | pyText += 'n = nuke.toNode(' + q + v[0] + q + ')\n' 1073 | pyText += 'n[' + q + v[1] + q + '].setValue(' + q + v[2] + q + ')\n' 1074 | 1075 | pyText += '\nnuke.scriptSave(' + q + scriptpath + q + ')' 1076 | 1077 | 1078 | try: 1079 | 1080 | # make py file 1081 | f = open(filePath, "w+") 1082 | # write 1083 | f.write(pyText) 1084 | # close 1085 | f.close() 1086 | 1087 | license = getLic() 1088 | args = [WIU_AppPath, license, '-q', filePath] 1089 | subprocess.call(args) 1090 | 1091 | # remove the remporary terminal file 1092 | try: 1093 | os.remove(filePath) 1094 | except Exception as e1: 1095 | err += 'Could not remove temporary file for relinking: ' + filePath + '\n' + str(e1) + '\n\n' 1096 | 1097 | # try to remove the autosave file 1098 | try: 1099 | os.remove(scriptpath + '~') 1100 | except Exception as e4: 1101 | pass 1102 | 1103 | except Exception as e2: 1104 | err += 'Could not write/execute temporary file for relinking: ' + filePath + '\n' + str(e2) + '\n\n' 1105 | 1106 | 1107 | if silent: 1108 | print(WIU_Log + 'End of Nuke block' + '\n') 1109 | 1110 | 1111 | return err 1112 | 1113 | 1114 | except Exception as e3: 1115 | return str(e3) 1116 | 1117 | 1118 | 1119 | # read comp 1120 | def Refresh(): 1121 | window.setEnabled(False) 1122 | WTotalProgress.setVisible(True) 1123 | threading.Thread(target=RefreshThreaded, args=()).start() 1124 | 1125 | 1126 | 1127 | # threaded refresh 1128 | def RefreshThreaded(): 1129 | 1130 | global WIU_MediaData 1131 | global WIU_OtherData 1132 | 1133 | # get all paths and information from the current comp 1134 | WIU_MediaData = ReadCompMediaData() 1135 | WIU_OtherData = ReadCompOtherData() 1136 | 1137 | # if silent, update ui 1138 | if not silent: 1139 | nuke.executeInMainThread(RefreshUI, args=()) 1140 | 1141 | # if not, return all data for silent mode 1142 | else: 1143 | refreshRtn = RefreshUI() 1144 | return refreshRtn 1145 | 1146 | 1147 | 1148 | # apply read data to WrapItUp window 1149 | def RefreshUI(): 1150 | 1151 | global WIU_MediaData 1152 | global WIU_OtherData 1153 | 1154 | global WIU_SilentReturn 1155 | global WIU_SilentList 1156 | 1157 | # if silent, return this list 1158 | WIU_SilentReturn = [] 1159 | 1160 | if not silent: 1161 | # clear list 1162 | WListCopyPaths.clear() 1163 | WListIgnorePaths.clear() 1164 | 1165 | # close if comp has not been saved 1166 | if not WIU_OtherData: 1167 | return 1168 | 1169 | # list count 1170 | iCopy = 0 1171 | item = "nk\t\t" + WIU_OtherData[0][0].split('/')[-1] if not silent else "nk\t\t\t\t" + WIU_OtherData[0][0].split('/')[-1] 1172 | if not silent: 1173 | # add current nuke script to copy list 1174 | WListCopyPaths.addItem(item) 1175 | WListCopyPaths.item(iCopy).setData(QtCore.Qt.UserRole, -1) 1176 | WListCopyPaths.item(iCopy).setForeground(QtGui.QColor(150, 150, 150)) 1177 | iCopy += 1 1178 | else: 1179 | WIU_SilentList.append([item, -1]) 1180 | WIU_SilentReturn.append(item) 1181 | 1182 | iCopy += RelinksInList() 1183 | iCopy += AddOnsInList() 1184 | 1185 | # go through each found fileknob value 1186 | iIgnore = 0 1187 | i = 0 1188 | for dataItem in WIU_MediaData: 1189 | 1190 | # check if the path exists 1191 | existsBool = dataItem[2] 1192 | disabled = '* ' if dataItem[7] else '' 1193 | exists = '' if existsBool else 'MISSING ' 1194 | 1195 | # make an item for the list 1196 | nodeName = getNodeNames(dataItem[0]) 1197 | extraTab = '\t' if len(nodeName) < 13 else '' 1198 | item = disabled + exists + nodeName + '\t' + extraTab + dataItem[3] if not silent else exists + nodeName + '\t\t' + extraTab + dataItem[3] 1199 | 1200 | if not silent: 1201 | 1202 | # add to the 'to copy' list 1203 | if(existsBool): 1204 | 1205 | # add item 1206 | WListCopyPaths.addItem(item) 1207 | 1208 | # add data to item 1209 | WListCopyPaths.item(iCopy).setData(QtCore.Qt.UserRole, i) 1210 | 1211 | # count 1212 | iCopy += 1 1213 | 1214 | # add to the 'ignore' list 1215 | else: 1216 | 1217 | # add item 1218 | WListIgnorePaths.addItem(item) 1219 | 1220 | # add data to item 1221 | WListIgnorePaths.item(iIgnore).setData(QtCore.Qt.UserRole, i) 1222 | 1223 | # grayed out colours 1224 | WListIgnorePaths.item(iIgnore).setForeground(QtGui.QColor(150, 150, 150)) 1225 | 1226 | # count 1227 | iIgnore += 1 1228 | 1229 | 1230 | else: 1231 | 1232 | if existsBool: 1233 | 1234 | WIU_SilentList.append([item, i]) 1235 | WIU_SilentReturn.append(item) 1236 | 1237 | 1238 | # overall count 1239 | i += 1 1240 | 1241 | if not silent: 1242 | 1243 | UpdateTotalSize() 1244 | 1245 | WTotalProgress.setVisible(False) 1246 | window.setEnabled(True) 1247 | 1248 | else: 1249 | 1250 | UpdateTotalSize() 1251 | 1252 | if startnow: 1253 | StartCopyRtn = StartCopy() 1254 | return StartCopyRtn 1255 | else: 1256 | return WIU_SilentReturn 1257 | 1258 | 1259 | 1260 | # move item from copy to ignore 1261 | def SendToIgnore(): 1262 | 1263 | for eachSelectedItem in WListCopyPaths.selectedItems(): 1264 | 1265 | # only copy over if allowed 1266 | selI = eachSelectedItem.data(QtCore.Qt.UserRole) 1267 | if selI >= 0: 1268 | WListCopyPaths.takeItem(WListCopyPaths.row(eachSelectedItem)) 1269 | WListIgnorePaths.addItem(eachSelectedItem) 1270 | 1271 | UpdateTotalSize() 1272 | 1273 | 1274 | 1275 | # move item from ignore to copy 1276 | def SendToCopy(): 1277 | 1278 | for eachSelectedItem in WListIgnorePaths.selectedItems(): 1279 | 1280 | # get data 1281 | selI = eachSelectedItem.data(QtCore.Qt.UserRole) 1282 | selData = WIU_MediaData[selI] 1283 | selExists = selData[2] 1284 | 1285 | # only copy over if it exists 1286 | if selExists: 1287 | WListIgnorePaths.takeItem(WListIgnorePaths.row(eachSelectedItem)) 1288 | WListCopyPaths.addItem(eachSelectedItem) 1289 | 1290 | UpdateTotalSize() 1291 | 1292 | 1293 | 1294 | # update total file size count 1295 | def UpdateTotalSize(): 1296 | 1297 | global WIU_TotalSize 1298 | 1299 | # add up all data from each item in the copy list 1300 | WIU_TotalSize = 0 1301 | 1302 | itemLen = WListCopyPaths.count() if not silent else len(WIU_SilentList) 1303 | for i in range(itemLen): 1304 | 1305 | itemData = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) if not silent else WIU_SilentList[i][1] 1306 | 1307 | Media = False 1308 | if itemData >= 0: 1309 | Media = True 1310 | 1311 | if Media: 1312 | for eachFile in WIU_MediaData[itemData][4]: 1313 | WIU_TotalSize += eachFile[1] 1314 | else: 1315 | WIU_TotalSize += WIU_OtherData[abs(itemData)-1][1] 1316 | 1317 | 1318 | # set label 1319 | if not silent: 1320 | WTotalCopySize.setText( BytesToString(WIU_TotalSize) ) 1321 | 1322 | 1323 | 1324 | # selection hanged on copy list 1325 | def CopyListSelectionChanged(): 1326 | 1327 | WListIgnorePaths.clearSelection() 1328 | UpdateLabels(True) 1329 | 1330 | 1331 | 1332 | # selection hanged on ignore list 1333 | def IgnoreListSelectionChanged(): 1334 | 1335 | WListCopyPaths.clearSelection() 1336 | UpdateLabels(False) 1337 | 1338 | 1339 | 1340 | # makes long path -> ../long path if it is too long 1341 | def shortenPath(strPath, iChars): 1342 | 1343 | newPath = '...' + strPath[-iChars:] 1344 | short = False 1345 | if len(strPath) > iChars: 1346 | short = True 1347 | 1348 | return newPath if short else strPath 1349 | 1350 | 1351 | 1352 | # convert a list of nodes to a string of node names - list of nodes, underscore, item index (for data check) 1353 | def getNodeNames(nodeList, us = False, i = 0): 1354 | 1355 | strNodes = '' 1356 | 1357 | maxNodeCount = 5 1358 | nodeCount = 0 1359 | 1360 | for eachNode in nodeList: 1361 | if(nodeCount >= maxNodeCount and i == 0): # check if there are too many node names 1362 | strNodes += 'etc' # 'etc' to indicate there were more nodes but the name got too long 1363 | return strNodes # return right away 1364 | 1365 | nodeCount += 1 1366 | 1367 | strNodes += (eachNode.fullName() if (i <= -5 or i >= 0) else eachNode) + ('_' if us else ' ') 1368 | 1369 | strNodes = strNodes[:-1] 1370 | 1371 | return strNodes 1372 | 1373 | 1374 | 1375 | # convert a list of knobs to a string of node names - list of knobs, underscore 1376 | def getKnobNames(knobList, us = False): 1377 | 1378 | strKnobs = '' 1379 | 1380 | for eachKnob in knobList: 1381 | strKnobs += eachKnob + ('_' if us else ' ') 1382 | 1383 | strKnobs = strKnobs[:-1] 1384 | 1385 | return strKnobs 1386 | 1387 | 1388 | 1389 | # update the labels underneath the lists 1390 | def UpdateLabels(CopyList): 1391 | 1392 | # disable button 1393 | WGoToNode.setEnabled(False) 1394 | 1395 | # get all selected objects 1396 | sel = [] 1397 | if(CopyList): 1398 | sel = WListCopyPaths.selectedItems() 1399 | else: 1400 | sel = WListIgnorePaths.selectedItems() 1401 | 1402 | if len(sel) == 1: 1403 | 1404 | # get last selected object 1405 | selVal = sel[0] 1406 | 1407 | # get data index 1408 | selI = selVal.data(QtCore.Qt.UserRole) 1409 | 1410 | # set data 1411 | selData = [] 1412 | selOtherData = [] 1413 | 1414 | Media = False 1415 | if selI >= 0: 1416 | Media = True 1417 | 1418 | curritempath = '' 1419 | packeditempath = '' 1420 | curritemfiles = '' 1421 | curritemsize = 0 1422 | 1423 | # get media data 1424 | if(Media): 1425 | selData = WIU_MediaData[selI] 1426 | curritempath = selData[3] 1427 | packeditempath = PackedPath( curritempath, nodeName=getNodeNames(selData[0], us=True), project_dir = selData[6] ) 1428 | curritemfiles = str( len(selData[4]) ) 1429 | curritemsize = selData[5] 1430 | 1431 | WGoToNode.setToolTip( getNodeNames(selData[0], us=False) ) 1432 | WGoToNode.setEnabled(True) 1433 | 1434 | # project directory prefix 1435 | if(selData[6]): 1436 | curritempath = ProjectDirectory(curritempath) 1437 | 1438 | 1439 | # get other data 1440 | else: 1441 | 1442 | # convert selI to positive integers, matching to the data array 1443 | selIConverted = abs(selI) - 1 1444 | 1445 | # get data 1446 | selData = WIU_OtherData[selIConverted] 1447 | 1448 | curritempath = selData[0] 1449 | packeditempath = PackedPath(curritempath, i = selI) 1450 | curritemfiles = '1' 1451 | 1452 | # set amount of files for fonts 1453 | if selI == -4: 1454 | curritemfiles = str(len(selData[2])) 1455 | packeditempath = WIU_PackedPath + '/' + curritempath[:-1].split('/')[-1] 1456 | 1457 | curritemsize = selData[1] 1458 | 1459 | 1460 | # TCL eval 1461 | curritempath = evalTCL(curritempath) 1462 | 1463 | 1464 | # set label texts and tooltips 1465 | labelLength = 100 1466 | 1467 | # get shortened path 1468 | curritempathShortened = shortenPath(curritempath, labelLength) 1469 | # set as text 1470 | WCurrItemPath.setText(curritempathShortened) 1471 | # set full path as tooltip 1472 | WCurrItemPath.setToolTip(curritempath) 1473 | 1474 | # get shortened path 1475 | packeditempathShortened = shortenPath(packeditempath, labelLength) 1476 | # set as text 1477 | WPackedItemPath.setText(packeditempathShortened) 1478 | # set full path as tooltip 1479 | WPackedItemPath.setToolTip(packeditempath) 1480 | 1481 | 1482 | WCurrItemFiles.setText(curritemfiles) 1483 | WCurrItemSize.setText( BytesToString(curritemsize) ) 1484 | 1485 | WIgnoredLabel.setVisible(not CopyList) 1486 | 1487 | WGoToFolder.setEnabled(True) 1488 | 1489 | else: 1490 | 1491 | selectItemStr = '-' 1492 | 1493 | WCurrItemPath.setText(selectItemStr) 1494 | WPackedItemPath.setText(selectItemStr) 1495 | WCurrItemFiles.setText(selectItemStr) 1496 | WCurrItemSize.setText(selectItemStr) 1497 | 1498 | WIgnoredLabel.setVisible(False) 1499 | 1500 | WGoToFolder.setEnabled(False) 1501 | 1502 | 1503 | 1504 | # update the labels underneath the lists 1505 | def UpdateItemInfo(n = False, pd = False): 1506 | 1507 | global WIU_ParentDirCount 1508 | global WIU_NodeNameFolder 1509 | 1510 | if pd: 1511 | WIU_ParentDirCount = WParentDirectories.value() 1512 | if n: 1513 | WIU_NodeNameFolder = WNodeNameFolder.isChecked() 1514 | 1515 | # check which list the currently selected item is in, and update its info 1516 | if WIgnoredLabel.isVisible(): 1517 | IgnoreListSelectionChanged() 1518 | else: 1519 | CopyListSelectionChanged() 1520 | 1521 | 1522 | 1523 | # on packed path changed 1524 | def PackedPathChanged(): 1525 | 1526 | # allow editing of global 1527 | global WIU_PackedPath 1528 | 1529 | # update WIU_PackedPath 1530 | WIU_PackedPath = WPackedPath.text() 1531 | 1532 | UpdateItemInfo() 1533 | 1534 | 1535 | 1536 | # arguments are switches for relink and relink, relative 1537 | def RelinksInList(r = False, rr = False): 1538 | 1539 | global WIU_Relink 1540 | global WIU_RelinkRelative 1541 | 1542 | if not silent: 1543 | 1544 | if r: 1545 | WIU_Relink = not WIU_Relink 1546 | if rr: 1547 | WIU_RelinkRelative = not WIU_RelinkRelative 1548 | 1549 | 1550 | # get each item 1551 | i = 0 1552 | while i < WListCopyPaths.count(): 1553 | 1554 | # get data 1555 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1556 | 1557 | # delete if relink 1558 | if d == -2 or d == -3: 1559 | WListCopyPaths.takeItem(i) 1560 | i = 0 1561 | else: 1562 | i += 1 1563 | 1564 | # if no UI 1565 | else: 1566 | WIU_Relink = relinked 1567 | WIU_RelinkRelative = relativerelinked 1568 | 1569 | 1570 | iCopyCount = 0 1571 | 1572 | 1573 | # add current nuke script to copy list, this time relative relinked 1574 | if WIU_RelinkRelative: 1575 | item = "nk (relative relinked)\t" + WIU_OtherData[0][0].split('/')[-1] 1576 | 1577 | if not silent: 1578 | index = 1 1579 | WListCopyPaths.insertItem(index, item) 1580 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -3) 1581 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1582 | iCopyCount += 1 1583 | 1584 | else: 1585 | WIU_SilentList.append([item, -3]) 1586 | WIU_SilentReturn.append(item) 1587 | 1588 | 1589 | # add current nuke script to copy list, this time relinked 1590 | if WIU_Relink: 1591 | item = "nk (relinked)\t\t" + WIU_OtherData[0][0].split('/')[-1] 1592 | 1593 | if not silent: 1594 | index = 1 1595 | WListCopyPaths.insertItem(index, item) 1596 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -2) 1597 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1598 | iCopyCount += 1 1599 | 1600 | else: 1601 | WIU_SilentList.append([item, -2]) 1602 | WIU_SilentReturn.append(item) 1603 | 1604 | 1605 | # for refresh function item count 1606 | return iCopyCount 1607 | 1608 | 1609 | 1610 | # show/hide addon items in list, arguments for toggles 1611 | def AddOnsInList(f = False, g = False): 1612 | 1613 | global WIU_Gizmo 1614 | global WIU_Fonts 1615 | 1616 | 1617 | if not silent: 1618 | 1619 | if g: 1620 | WIU_Gizmo = not WIU_Gizmo 1621 | if f: 1622 | WIU_Fonts = not WIU_Fonts 1623 | 1624 | # remove all addon items 1625 | i = 0 1626 | while i < WListCopyPaths.count(): 1627 | 1628 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1629 | 1630 | if d <= -4: 1631 | WListCopyPaths.takeItem(i) 1632 | i = 0 1633 | else: 1634 | i += 1 1635 | 1636 | # if no UI is loaded 1637 | else: 1638 | WIU_Gizmo = gizmos 1639 | WIU_Fonts = fonts 1640 | 1641 | 1642 | iCopyCount = 0 1643 | 1644 | # determine index of items 1645 | index = 1 1646 | if not silent: 1647 | if WRelativeRelink.isChecked(): 1648 | index += 1 1649 | 1650 | if WRelinkPaths.isChecked(): 1651 | index += 1 1652 | 1653 | if WIU_Gizmo: 1654 | 1655 | # iterate through all items in WIU_OtherData, except for the first 4 (0 - 3, which are nuke script, - relinked, - relinked relative, font) 1656 | for eachGizmo in range(len(WIU_OtherData) - 4): 1657 | 1658 | item = "gizmo\t\t" + WIU_OtherData[4 + eachGizmo][0] if not silent else "gizmo\t\t\t" + WIU_OtherData[4 + eachGizmo][0] 1659 | 1660 | if not silent: 1661 | 1662 | WListCopyPaths.insertItem(index, item) 1663 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -5 - eachGizmo) 1664 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1665 | iCopyCount += 1 1666 | 1667 | else: 1668 | 1669 | WIU_SilentList.append([item, -5 - eachGizmo]) 1670 | WIU_SilentReturn.append(item) 1671 | 1672 | FontPathExists = os.path.isdir(WIU_OtherData[3][0]) 1673 | if WIU_Fonts and FontPathExists: 1674 | 1675 | item = "font folder\t\t" + WIU_OtherData[3][0] if not silent else "font folder\t\t\t" + WIU_OtherData[3][0] 1676 | 1677 | if not silent: 1678 | 1679 | WListCopyPaths.insertItem(index, item) 1680 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -4) 1681 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1682 | iCopyCount += 1 1683 | 1684 | else: 1685 | 1686 | WIU_SilentList.append([item, -4]) 1687 | WIU_SilentReturn.append(item) 1688 | 1689 | 1690 | # for refresh function item count 1691 | return iCopyCount 1692 | 1693 | 1694 | 1695 | def ChoosePackedPath(): 1696 | 1697 | # open nuke file dialog 1698 | chosenPath = nuke.getFilename('WrapItUp - choose the folder to copy the script and all media to') 1699 | 1700 | if chosenPath is not None: 1701 | 1702 | # make sure it is an existing folder 1703 | if os.path.isfile(chosenPath): 1704 | chosenPath = os.path.dirname(chosenPath) 1705 | 1706 | # cut off the last / if it is there 1707 | if chosenPath.endswith('/'): 1708 | chosenPath = chosenPath[:-1] 1709 | 1710 | # set value to text knob 1711 | if os.path.isdir(chosenPath): 1712 | # set packed path 1713 | WPackedPath.setText(chosenPath) 1714 | 1715 | # path does not exist, ask to create the folders instead 1716 | else: 1717 | q = nuke.ask(u'路径不存在:\n\n' + chosenPath + '\n\n是否仍然选择这个路径?打包文件时会创建任何不存在的文件夹.') 1718 | if q: 1719 | if chosenPath.endswith('/'): 1720 | chosenPath = chosenPath[:-1] 1721 | WPackedPath.setText(chosenPath) 1722 | 1723 | 1724 | # regain focus on form 1725 | window.activateWindow() 1726 | 1727 | 1728 | 1729 | # function to control buttons on form 1730 | def ButtonsAllowed(YesOrNo): 1731 | 1732 | # disable all buttons that could interfere (except for the interrupt button of course) 1733 | WSendToCopy.setEnabled(YesOrNo) 1734 | WSendToIgnore.setEnabled(YesOrNo) 1735 | WStart.setEnabled(YesOrNo) 1736 | WRefresh.setEnabled(YesOrNo) 1737 | WParentDirectories.setEnabled(YesOrNo) 1738 | WRelinkPaths.setEnabled(YesOrNo) 1739 | WRelativeRelink.setEnabled(YesOrNo) 1740 | WNodeNameFolder.setEnabled(YesOrNo) 1741 | WPackedPath.setEnabled(YesOrNo) 1742 | WChoosePackedPathButton.setEnabled(YesOrNo) 1743 | WLParentDirectories.setEnabled(YesOrNo) 1744 | WLPath.setEnabled(YesOrNo) 1745 | WContinueOnError.setEnabled(YesOrNo) 1746 | WCSVSeparator.setEnabled(YesOrNo) 1747 | WLCSVSeparator.setEnabled(YesOrNo) 1748 | WLicense.setEnabled(YesOrNo) 1749 | WLLicense.setEnabled(YesOrNo) 1750 | WExitOnFinish.setEnabled(YesOrNo) 1751 | WCopyFontDir.setEnabled(YesOrNo) 1752 | WCopyGizmos.setEnabled(YesOrNo) 1753 | 1754 | # make progress bar visible 1755 | WTotalProgress.setVisible(not YesOrNo) 1756 | WItemProgress.setVisible(not YesOrNo) 1757 | WCurrentCopyItem.setVisible(not YesOrNo) 1758 | 1759 | 1760 | 1761 | # prepare list to copy 1762 | def PrepareCopy(): 1763 | 1764 | global WIU_TotalSize 1765 | global WIU_SilentList 1766 | global WIU_MediaDataNotIgnored 1767 | 1768 | # bool for ProcessFiles 1769 | relinkFonts = False 1770 | 1771 | # get all items in data list 1772 | itemsToCopy = [] 1773 | listLen = WListCopyPaths.count() if not silent else len(WIU_SilentList) 1774 | for i in range(listLen): 1775 | 1776 | # get index 1777 | dataItem = 0 1778 | if not silent: 1779 | dataItem = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1780 | else: 1781 | dataItem = WIU_SilentList[i][1] 1782 | 1783 | # assign custom data for nuke script to item 1784 | # make list with item index and data combined, so the index does not get lost 1785 | if(dataItem < 0): 1786 | itemsToCopy.append( [dataItem, WIU_OtherData[abs(dataItem) - 1]] ) 1787 | # all other media files 1788 | else: 1789 | itemsToCopy.append( [dataItem, WIU_MediaData[dataItem]] ) 1790 | 1791 | # store this index for relinking only relevant media 1792 | WIU_MediaDataNotIgnored.append(WIU_MediaData[dataItem]) 1793 | 1794 | 1795 | 1796 | 1797 | # get all files involved 1798 | fileList = [] 1799 | i = 0 1800 | addedSizes = 0.0 1801 | 1802 | for j in itemsToCopy: 1803 | 1804 | # index, data 1805 | k = j[0] 1806 | l = j[1] 1807 | 1808 | # media 1809 | if k >= 0 and media: 1810 | 1811 | # item size 1812 | itemSizeCounting = 0 1813 | 1814 | # get list of each file, file size 1815 | for eachFileS in l[4]: 1816 | 1817 | # get path of destination file path 1818 | eachFileTo = PackedPath(eachFileS[0], nodeName = getNodeNames(l[0], us = True), project_dir = l[6]) 1819 | 1820 | # count sizes together 1821 | itemSizeCounting += eachFileS[1] 1822 | 1823 | # single files of 0 bytes should still be copied (even though it does not make sense to have them in your script), make them 1 byte for progress bar 1824 | currSize = l[5] if int(l[5]) > 0 else 1 1825 | # get ratio for progress bar % 1826 | sequenceSizeRatio = int (float(itemSizeCounting) / float(currSize) * 100) 1827 | 1828 | # set file, file path to copy to, list item index, file size in bytes, size ratio within sequence for progress bar, nodes, knobs, original index 1829 | fileList.append([ eachFileS[0], eachFileTo, i, eachFileS[1], sequenceSizeRatio, l[0], l[1], k ]) 1830 | 1831 | 1832 | # nuke script 1833 | if k == -1: 1834 | 1835 | filePath = l[0] 1836 | fileSize = l[1] 1837 | fileTo = PackedPath(filePath, i = k) 1838 | 1839 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1840 | 1841 | # nuke script relinked 1842 | if k == -2: 1843 | 1844 | filePath = l[0] 1845 | fileSize = l[1] 1846 | fileTo = PackedPath(filePath, i = k) 1847 | 1848 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1849 | 1850 | # nuke script relinked relative 1851 | if k == -3: 1852 | 1853 | filePath = l[0] 1854 | fileSize = l[1] 1855 | fileTo = PackedPath(filePath, i = k) 1856 | 1857 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1858 | 1859 | # font directory 1860 | if k == -4: 1861 | 1862 | # set bool 1863 | relinkFonts = True 1864 | 1865 | # item size 1866 | itemSizeCounting = 0 1867 | 1868 | # get each font, size combination 1869 | for eachFileS in l[2]: 1870 | 1871 | # get path of destination file path 1872 | if l[0].endswith('/'): 1873 | l[0] = l[0][:-1] 1874 | eachFileTo = PackedPath(eachFileS[0], i = k, fontFolder = l[0]) 1875 | 1876 | # count sizes together 1877 | itemSizeCounting += eachFileS[1] 1878 | 1879 | # progress bar value (l[1] if not 0, else 1) 1880 | maxVal = float(l[1] * 100) 1881 | maxVal = 1 if maxVal == 0 else maxVal 1882 | sequenceSizeRatio = int ( float(itemSizeCounting) / (maxVal) ) 1883 | 1884 | # set file, file path to copy to, list item index, file size in bytes, size ratio within sequence for progress bar, nodes, knobs, original index 1885 | fileList.append( [eachFileS[0], eachFileTo, i, eachFileS[1], sequenceSizeRatio, ['root'], ['free_type_font_path'], k] ) 1886 | 1887 | # gizmo 1888 | if k <= -5: 1889 | 1890 | filePath = l[0] 1891 | fileSize = l[1] 1892 | nodes = l[2] 1893 | fileTo = PackedPath(filePath, i = k) 1894 | 1895 | fileList.append([ filePath, fileTo, i, fileSize, 100, nodes, ['-'], k ]) 1896 | 1897 | 1898 | # count 1899 | i += 1 1900 | 1901 | 1902 | return [fileList, relinkFonts] 1903 | 1904 | 1905 | 1906 | # function to append text to log file 1907 | def WriteLog(logText, first = False): 1908 | 1909 | # which file 1910 | filePath = WIU_PackedPath + '/log.csv' 1911 | 1912 | # try to remove it if logging just started 1913 | if first: 1914 | 1915 | try: 1916 | os.remove(filePath) 1917 | except Exception as e: 1918 | pass 1919 | 1920 | 1921 | try: 1922 | 1923 | # open file 1924 | f = open(filePath, "a+") 1925 | 1926 | # write and close 1927 | f.write(logText + '\n') 1928 | f.close() 1929 | 1930 | except Exception as e: 1931 | pass 1932 | 1933 | 1934 | 1935 | # convert data to CSV format 1936 | def CSV(values): 1937 | 1938 | CSVtext = '' 1939 | csv = WCSVSeparator.currentText() if not silent else (',' if csvcommas else ';') 1940 | for d in values: 1941 | CSVtext += '\"' + str(d) + '\"' + (csv) 1942 | 1943 | return CSVtext 1944 | 1945 | 1946 | 1947 | def getLic(): 1948 | 1949 | license = WLicense.currentText() if not silent else ('-ti' if licinteractive else '-t') 1950 | return license 1951 | 1952 | 1953 | 1954 | # threaded function for copying/relinking - list of files to process with all necessary data, fonts should be relinked (bool) 1955 | def ThreadedCopy(fileList, relinkFonts): 1956 | 1957 | # error count 1958 | totalErrorCount = 0 1959 | 1960 | # count size 1961 | totalSizeCount = 0 1962 | totalSizeStored = WIU_TotalSize 1963 | 1964 | listCount = (WListCopyPaths.count() if not silent else len(WIU_SilentList)) - 1 1965 | 1966 | # write first lines of log 1967 | nukever = str(nuke.NUKE_VERSION_STRING) 1968 | nukescript = nuke.root().name() if not silent else nk 1969 | params = CSV(["Used command-line/python function", str(silent)]) + '\n' + CSV(["Script path", nukescript]) + '\n' + CSV(["Folders with node names", str(WIU_NodeNameFolder)]) + '\n' + CSV(["Parent directory count", str(WIU_ParentDirCount)]) + '\n' + CSV(["Relinked", str(WIU_Relink)]) + '\n' + CSV(["Relative relinked", str(WIU_RelinkRelative)]) + '\n' + CSV(["Fonts", str(WIU_Fonts)]) + '\n' + CSV(["Gizmos", str(WIU_Gizmo)]) + '\n' + CSV(["License", 'nuke_i' if getLic() == '-ti' else 'nuke_r']) 1970 | WriteLog( WIU_Title + '\n' + 'Nuke version: ' + nukever + '\n\n' + params + '\n\n\n' + CSV(['TIME', 'STATUS', 'FILE (FROM)', 'FILE (TO)', 'NODE', 'KNOB', 'SIZE', 'RETURN']) + '\n', first = True ) 1971 | 1972 | # copy files 1973 | prevI = -1 1974 | firstInit = True 1975 | i = 0 1976 | iM = len(fileList) 1977 | c = False 1978 | lastItemFailed = False 1979 | for f in fileList: 1980 | 1981 | # get current time 1982 | timestr = time.strftime("%c") 1983 | 1984 | # interrupt check 1985 | if WIU_Interrupted: 1986 | WriteLog( CSV([timestr, 'PROCESS WAS CANCELLED BY USER']) ) 1987 | nuke.executeInMainThread(Finished, args=('USER')) 1988 | sys.exit() 1989 | 1990 | 1991 | relinkMethod = 0 if f[7] == -2 else (1 if f[7] == -3 else -1) # 0 if relink, 1 if relink relative, -1 if none 1992 | suffix = ' copy/relink' if relinkMethod is not -1 else '' # suffix for relinked nk scripts 1993 | labelText = shortenPath(f[0] + suffix, 20) # get shorter path for current copying file, add suffix 1994 | 1995 | if not silent: 1996 | 1997 | # set label text 1998 | nuke.executeInMainThread(SetCurrCopyItemLabel, args=(labelText, f[0])) 1999 | 2000 | # set working colour 2001 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], True)) 2002 | 2003 | else: 2004 | 2005 | # append size to label text for console 2006 | labelText += ' (' + BytesToString(f[3]) + ')' 2007 | 2008 | 2009 | # start process 2010 | writeInit = f[7] <= -5 # True if in gizmo range of list 2011 | result = ProcessFile(f[0], f[1], writeInit, firstInit, relinkMethod, relinkFonts) 2012 | if writeInit: 2013 | firstInit = False 2014 | 2015 | # add to size count 2016 | totalSizeCount += f[3] 2017 | 2018 | # write log 2019 | if result == '': 2020 | 2021 | WriteLog( CSV([timestr, 'SUCCES', f[0], f[1], getNodeNames(f[5], i=f[7]), getKnobNames(f[6]), BytesToString(f[3])]) ) 2022 | 2023 | # make green 2024 | if(f[2] > prevI): 2025 | if not silent: 2026 | if not lastItemFailed: 2027 | nuke.executeInMainThread(ChangeItemColour, args = (prevI, False, True)) 2028 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], False, True)) 2029 | lastItemFailed = False 2030 | prevI = f[2] 2031 | 2032 | # on error 2033 | else: 2034 | 2035 | WriteLog( CSV([timestr, 'FAILED', f[0], f[1], getNodeNames(f[5], i=f[7]), getKnobNames(f[6]), BytesToString(f[3]), result]) ) 2036 | 2037 | if not silent: 2038 | if not WContinueOnError.isChecked(): 2039 | nuke.executeInMainThread(Finished, args = ([f[0] + '\n\n' + result])) 2040 | 2041 | # make red 2042 | if(f[2] > prevI): 2043 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], False, False)) 2044 | lastItemFailed = True 2045 | prevI = f[2] 2046 | 2047 | totalErrorCount += 1 2048 | 2049 | # set total progress bar 2050 | sizeRatio = float( totalSizeCount ) / float( totalSizeStored ) 2051 | progressVal = int( sizeRatio * 100 ) 2052 | 2053 | 2054 | # set item progress bar 2055 | if not silent: 2056 | nuke.executeInMainThread(ItemProgressUpdate, args = (f[4])) 2057 | nuke.executeInMainThread(TotalProgressUpdate, args = (progressVal)) 2058 | 2059 | # print progress 2060 | else: 2061 | if not (f[7] <= -1 and f[7] >= -3): 2062 | if not c: 2063 | c = True 2064 | print('') 2065 | 2066 | if i == iM - 1: 2067 | labelText = ' ' 2068 | sys.stdout.write(WIU_Log + "Copying files: %d%% \t %s \r" % (progressVal, labelText) ) 2069 | sys.stdout.flush() 2070 | 2071 | i += 1 2072 | 2073 | WriteLog( CSV([timestr, 'FINISHED!']) ) 2074 | 2075 | if not silent: 2076 | nuke.executeInMainThread(Finished, args = ('' if totalErrorCount == 0 else '.')) 2077 | else: 2078 | print('\n\n\n' + WIU_Log + (('Finished! No errors. Out folder path:\n' + WIU_Log + out) if totalErrorCount == 0 else 'Finished copying! There were some errors. Check the log in the out folder:\n' + WIU_Log + out)) 2079 | 2080 | 2081 | 2082 | # set current copy item texts 2083 | def SetCurrCopyItemLabel(strText, strToolTip): 2084 | 2085 | WCurrentCopyItem.setText(strText) 2086 | WCurrentCopyItem.setToolTip(strToolTip) 2087 | 2088 | 2089 | 2090 | # total progress bar update on main thread 2091 | def TotalProgressUpdate(NewValue): 2092 | 2093 | WTotalProgress.setValue(NewValue) 2094 | 2095 | 2096 | 2097 | # item progress bar update on main thread 2098 | def ItemProgressUpdate(NewValue): 2099 | 2100 | WItemProgress.setValue(NewValue) 2101 | 2102 | 2103 | 2104 | # progress bar update on main thread 2105 | def ChangeItemColour(i, loading, Succeeded=False): 2106 | 2107 | C = [] 2108 | 2109 | if Succeeded: 2110 | C = [0, 255, 0] 2111 | else: 2112 | if loading: 2113 | C = [255, 215, 0] 2114 | else: 2115 | C = [255, 0, 0] 2116 | 2117 | 2118 | # 'disabled' colour 2119 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 2120 | if d < 0 and not loading: 2121 | C = [round(C[0]*.5), round(C[1]*.5), round(C[2]*.5)] 2122 | 2123 | WListCopyPaths.item(i).setForeground(QtGui.QColor(C[0], C[1], C[2])) 2124 | 2125 | 2126 | 2127 | # function to call on finish 2128 | def Finished(isError): 2129 | 2130 | global WIU_Interrupted 2131 | global WIU_Copying 2132 | 2133 | WIU_Interrupted = False 2134 | WIU_Copying = False 2135 | 2136 | 2137 | # exit window when not interrupted by user and exit on finish is enabled 2138 | if ((isError is not 'USER') and (WExitOnFinish.isChecked())): 2139 | 2140 | # do not ask to save script 2141 | try: 2142 | nuke.toNode('root').setModified(False) 2143 | except Exception as e: 2144 | pass 2145 | # exit 2146 | nuke.scriptExit() 2147 | 2148 | 2149 | else: 2150 | 2151 | if(isError == ''): 2152 | 2153 | window.close() 2154 | nuke.message(u"打包完成!没有错误。\n文件夹会马上打开.") 2155 | OpenFolder(WIU_PackedPath) 2156 | 2157 | 2158 | elif(isError == '.'): 2159 | 2160 | window.close() 2161 | nuke.message(u"打包完成! 发生了一些错误。\n文件夹会马上打开,有关错误的详细信息,请参阅日志文件。!") 2162 | OpenFolder(WIU_PackedPath) 2163 | 2164 | elif(isError == 'USER'): 2165 | 2166 | nuke.message("打包已取消! 文件夹会立即打开.") 2167 | window.activateWindow() 2168 | OpenFolder(WIU_PackedPath) 2169 | 2170 | WInterrupt.setEnabled(True) 2171 | ButtonsAllowed(True) 2172 | Refresh() 2173 | 2174 | else: 2175 | 2176 | nuke.message("ERROR: " + isError + '\n\nSee ./log.txt for more information.') 2177 | WInterrupt.setEnabled(True) 2178 | ButtonsAllowed(True) 2179 | window.activateWindow() 2180 | OpenFolder(WIU_PackedPath) 2181 | Refresh() 2182 | 2183 | 2184 | 2185 | # open folder 2186 | def OpenFolder(folderPath = ''): 2187 | 2188 | if folderPath == '': 2189 | folderPath = WCurrItemPath.toolTip() 2190 | oFolderPath = folderPath 2191 | 2192 | try: 2193 | # get parent dir 2194 | if not os.path.isdir(folderPath): 2195 | folderPath = os.path.dirname(folderPath) 2196 | 2197 | # get project directory folder 2198 | if not os.path.isdir(folderPath): 2199 | folderPath = ProjectDirectory(oFolderPath) 2200 | 2201 | 2202 | if os.path.isdir(folderPath): 2203 | 2204 | if sys.platform == 'win32': 2205 | folderPath = folderPath.replace('/', '\\') 2206 | os.startfile(folderPath) 2207 | else: 2208 | opener ="open" if sys.platform == "darwin" else "xdg-open" 2209 | subprocess.call([opener, folderPath]) 2210 | 2211 | 2212 | else: 2213 | print(folderPath) 2214 | 2215 | except Exception as e: 2216 | pass 2217 | 2218 | 2219 | 2220 | def ToNode(): 2221 | 2222 | # select the nodes 2223 | for i in nuke.allNodes(): 2224 | try: 2225 | i.setSelected(False) 2226 | except Exception as e: 2227 | pass 2228 | 2229 | 2230 | for eachNode in WGoToNode.toolTip().split(' '): 2231 | n = nuke.toNode(eachNode) 2232 | try: 2233 | n.setSelected(True) 2234 | except Exception as e: 2235 | pass 2236 | 2237 | # zoom 2238 | nuke.zoomToFitSelected() 2239 | 2240 | 2241 | 2242 | # start copy 2243 | def StartCopy(): 2244 | 2245 | global WIU_Copying 2246 | global WIU_PackedPath 2247 | 2248 | # check 2249 | q = True 2250 | if not silent: 2251 | q = nuke.ask(u"开始打包 " + WTotalCopySize.text() + '?\n' + '打包路径: ' + WIU_PackedPath + '\n如果选定的打包路径内有任何现有文件都会被覆盖.') 2252 | window.activateWindow() 2253 | 2254 | # set out folder 2255 | else: 2256 | WIU_PackedPath = out 2257 | 2258 | if q: 2259 | 2260 | # disable ui 2261 | if not silent: 2262 | ButtonsAllowed(False) 2263 | 2264 | # get list to copy 2265 | preparedCopy = PrepareCopy() 2266 | fileList = preparedCopy[0] 2267 | relinkFonts = preparedCopy[1] 2268 | 2269 | if not silent: 2270 | # run process on different thread 2271 | threading.Thread(target=ThreadedCopy, args=([fileList, relinkFonts])).start() 2272 | else: 2273 | # run threaded copy on main thread in terminal 2274 | ThreadedCopy(fileList, relinkFonts) 2275 | 2276 | WIU_Copying = True 2277 | 2278 | if silent: 2279 | return WIU_SilentReturn 2280 | 2281 | 2282 | 2283 | # interrupt 2284 | def StopCopy(): 2285 | 2286 | global WIU_Interrupted 2287 | global WIU_Copying 2288 | 2289 | # check 2290 | if WIU_Copying: 2291 | 2292 | question = u"确定取消?" 2293 | q = nuke.ask(question) 2294 | 2295 | window.activateWindow() 2296 | 2297 | if q: 2298 | 2299 | WCurrentCopyItem.setText("Cancelling...") 2300 | WCurrentCopyItem.setToolTip('Waiting for the current file copy to complete before stopping the copy process.') 2301 | WIU_Interrupted = True 2302 | WInterrupt.setEnabled(False) 2303 | 2304 | else: 2305 | 2306 | window.close() 2307 | 2308 | 2309 | 2310 | # open root folder 2311 | def GoToRootFolder(): 2312 | if not WIU_PackedPath == '': 2313 | OpenFolder(WIU_PackedPath) 2314 | 2315 | 2316 | 2317 | # catch window closing if the rejected button was not pressed 2318 | def exitForm(): 2319 | if WIU_Copying: 2320 | window.show() 2321 | 2322 | 2323 | 2324 | # show ui if not silent mode 2325 | if not silent: 2326 | 2327 | window = QtWidgets.QDialog() 2328 | ui = Ui_Dialog() 2329 | ui.setupUi(window) 2330 | 2331 | 2332 | # define widgets 2333 | WLPath = window.findChild(QtWidgets.QLabel, "LPath") 2334 | WPackedPath = window.findChild(QtWidgets.QLineEdit, "PackedPath") 2335 | WGoToRootFolder = window.findChild(QtWidgets.QPushButton, "GoToRootFolder") 2336 | WChoosePackedPathButton = window.findChild(QtWidgets.QPushButton, "ChoosePackedPathButton") 2337 | 2338 | WSendToIgnore = window.findChild(QtWidgets.QPushButton, "SendToIgnore") 2339 | WSendToCopy = window.findChild(QtWidgets.QPushButton, "SendToCopy") 2340 | WRefresh = window.findChild(QtWidgets.QPushButton, "Refresh") 2341 | WGoToFolder = window.findChild(QtWidgets.QPushButton, "GoToFolder") 2342 | WGoToNode = window.findChild(QtWidgets.QPushButton, "GoToNode") 2343 | WListCopyPaths = window.findChild(QtWidgets.QListWidget, "ListCopyPaths") 2344 | WListIgnorePaths = window.findChild(QtWidgets.QListWidget, "ListIgnorePaths") 2345 | 2346 | WRelinkPaths = window.findChild(QtWidgets.QCheckBox, "RelinkPaths") 2347 | WRelativeRelink = window.findChild(QtWidgets.QCheckBox, "RelativeRelink") 2348 | WNodeNameFolder = window.findChild(QtWidgets.QCheckBox, "NodeNameFolder") 2349 | WParentDirectories = window.findChild(QtWidgets.QSpinBox, "ParentDirectories") 2350 | WLParentDirectories = window.findChild(QtWidgets.QLabel, "LParentDirectories") 2351 | 2352 | WCopyFontDir = window.findChild(QtWidgets.QCheckBox, "CopyFontDir") 2353 | WCopyGizmos = window.findChild(QtWidgets.QCheckBox, "CopyGizmos") 2354 | 2355 | WContinueOnError = window.findChild(QtWidgets.QCheckBox, "ContinueOnError") 2356 | WExitOnFinish = window.findChild(QtWidgets.QCheckBox, "ExitOnFinish") 2357 | WCSVSeparator = window.findChild(QtWidgets.QComboBox, "CSVSeparator") 2358 | WLCSVSeparator = window.findChild(QtWidgets.QLabel, "LCSVSeparator") 2359 | WLicense = window.findChild(QtWidgets.QComboBox, "License") 2360 | WLLicense = window.findChild(QtWidgets.QLabel, "LLicense") 2361 | 2362 | WIgnoredLabel = window.findChild(QtWidgets.QLabel, "IgnoredLabel") 2363 | WCurrItemPath = window.findChild(QtWidgets.QLabel, "CurrItemPath") 2364 | WPackedItemPath = window.findChild(QtWidgets.QLabel, "PackedItemPath") 2365 | WCurrItemFiles = window.findChild(QtWidgets.QLabel, "CurrItemFiles") 2366 | WCurrItemSize = window.findChild(QtWidgets.QLabel, "CurrItemSize") 2367 | 2368 | WCurrentCopyItem = window.findChild(QtWidgets.QLabel, "CurrentCopyItem") 2369 | WTotalProgress = window.findChild(QtWidgets.QProgressBar, "TotalProgress") 2370 | WItemProgress = window.findChild(QtWidgets.QProgressBar, "ItemProgress") 2371 | 2372 | WTotalCopySize = window.findChild(QtWidgets.QLabel, "TotalCopySize") 2373 | WStart = window.findChild(QtWidgets.QPushButton, "Start") 2374 | WInterrupt = window.findChild(QtWidgets.QPushButton, "Interrupt") 2375 | 2376 | WLWebsite = window.findChild(QtWidgets.QLabel, "LWebsite") 2377 | 2378 | 2379 | # connect widgets to functions 2380 | WPackedPath.textChanged.connect(lambda:PackedPathChanged()) 2381 | WGoToRootFolder.clicked.connect(lambda:GoToRootFolder()) 2382 | WChoosePackedPathButton.clicked.connect(lambda:ChoosePackedPath()) 2383 | 2384 | WSendToIgnore.clicked.connect(lambda:SendToIgnore()) 2385 | WSendToCopy.clicked.connect(lambda:SendToCopy()) 2386 | WRefresh.clicked.connect(lambda:Refresh()) 2387 | WGoToFolder.clicked.connect(lambda:OpenFolder()) 2388 | WGoToNode.clicked.connect(lambda:ToNode()) 2389 | WListCopyPaths.itemSelectionChanged.connect(lambda:CopyListSelectionChanged()) 2390 | WListIgnorePaths.itemSelectionChanged.connect(lambda:IgnoreListSelectionChanged()) 2391 | 2392 | WRelinkPaths.stateChanged.connect(lambda:RelinksInList(r = True)) 2393 | WRelativeRelink.stateChanged.connect(lambda:RelinksInList(rr = True)) 2394 | WNodeNameFolder.stateChanged.connect(lambda:UpdateItemInfo(n = True)) 2395 | WParentDirectories.valueChanged.connect(lambda:UpdateItemInfo(pd = True)) 2396 | 2397 | WCopyFontDir.stateChanged.connect(lambda:AddOnsInList(f = True)) 2398 | WCopyGizmos.stateChanged.connect(lambda:AddOnsInList(g = True)) 2399 | 2400 | WStart.clicked.connect(lambda:StartCopy()) 2401 | WInterrupt.clicked.connect(lambda:StopCopy()) 2402 | 2403 | 2404 | # disable esc 2405 | window.rejected.connect(lambda:exitForm()) 2406 | 2407 | # disable window resizing 2408 | window.setFixedSize(window.size()) 2409 | 2410 | # hide ui elements, set enable/disable state 2411 | ButtonsAllowed(True) 2412 | WIgnoredLabel.setVisible(False) 2413 | 2414 | 2415 | # show the UI window 2416 | window.show() 2417 | 2418 | # read comp data 2419 | Refresh() 2420 | 2421 | else: 2422 | 2423 | # open chosen nuke script 2424 | nuke.scriptOpen(nk) 2425 | 2426 | # non-threaded version of same function 2427 | data = RefreshThreaded() 2428 | return data 2429 | 2430 | 2431 | 2432 | # python function start 2433 | def WrapItUp(fromterminal = False, nk = '', startnow = False, out = '', nodenamefolder = True, parentdircount = 3, relinked = True, relativerelinked = True, media = True, skipdisablednodes = False, fonts = True, gizmos = True, csvcommas = False, licinteractive = False): 2434 | 2435 | global WIU_AppPath 2436 | 2437 | # set nuke app path if not running from terminal 2438 | if not fromterminal: 2439 | WIU_AppPath = nuke.EXE_PATH 2440 | 2441 | # if run from python function or terminal 2442 | if out is not '': 2443 | 2444 | # empty line 2445 | print('') 2446 | 2447 | # starting line 2448 | if fromterminal: 2449 | print('\n' + WIU_Log + 'Running from terminal (no UI)...') 2450 | else: 2451 | print('\n' + WIU_Log + 'Running from Python function (no UI)...') 2452 | 2453 | 2454 | # set to current nuke script if no custom one has been defined 2455 | if nk == '': 2456 | nk = nuke.scriptName() 2457 | nk = nk.replace('\\', '/') 2458 | 2459 | # error bool 2460 | err = False 2461 | 2462 | # check if out path exists, convert to backslashes 2463 | out = out.replace('\\', '/') 2464 | if not os.path.isdir(out): 2465 | err = True 2466 | print(WIU_Log + 'ERROR: Folder does not exist: ' + out) 2467 | 2468 | if not os.path.isfile(nk): 2469 | err = True 2470 | print(WIU_Log + 'ERROR: Nuke script does not exist: ' + nk) 2471 | 2472 | # check if parent directory count is a valid number 2473 | if parentdircount < 1 or parentdircount > 99: 2474 | err = True 2475 | print(WIU_Log + 'ERROR: directory count (' + str(parentdircount) + ') should be in range 1-99!') 2476 | 2477 | 2478 | # silent mode 2479 | silent = True 2480 | 2481 | 2482 | # exit on error 2483 | if err: 2484 | return 2485 | 2486 | # start 2487 | else: 2488 | 2489 | # param list 2490 | p = [['Nuke script\t', nk], 2491 | ['Output path\t', out], 2492 | ['Node name folders', nodenamefolder], 2493 | ['Parent dir count', parentdircount], 2494 | ['Relink\t', relinked], 2495 | ['Relink relative', relativerelinked], 2496 | ['media\t', media], 2497 | ['skipdisablednodes\t', skipdisablednodes], 2498 | ['Fonts\t', fonts], 2499 | ['Gizmo files\t', gizmos], 2500 | ['CSV commas\t', csvcommas], 2501 | ['Interactive license', licinteractive]] 2502 | 2503 | # param string 2504 | param = '\n' + WIU_Log + 'Selected parameters' 2505 | for i in p: 2506 | param += '\n' + WIU_Log + i[0] + '\t\t' + str(i[1]) 2507 | print(param) 2508 | 2509 | # only print preview 2510 | returnedFiles = _Start(silent, nk, False, out, nodenamefolder, parentdircount, relinked, relativerelinked, media, skipdisablednodes, fonts, gizmos, csvcommas, licinteractive) 2511 | returnedStr = '' 2512 | for i in returnedFiles: 2513 | returnedStr += '\n' + WIU_Log + str(i) 2514 | 2515 | print('\n' + WIU_Log + 'Found files (' + BytesToString(WIU_TotalSize) + '):\n' + returnedStr + '\n') 2516 | 2517 | 2518 | # if starting right away 2519 | if startnow: 2520 | 2521 | # start 2522 | print('\n' + WIU_Log + 'Starting...' + '\n') 2523 | 2524 | # begin 2525 | _Start(silent, nk, startnow, out, nodenamefolder, parentdircount, relinked, relativerelinked, media, skipdisablednodes, fonts, gizmos, csvcommas, licinteractive) 2526 | 2527 | 2528 | # if running with UI in Nuke 2529 | if out == '' and not fromterminal: 2530 | 2531 | # starting in UI mode w/o parameters 2532 | _Start() 2533 | 2534 | 2535 | 2536 | # autostart (if not imported) 2537 | if __name__ == "__main__": 2538 | 2539 | # get all args 2540 | c = nuke.rawArgs 2541 | 2542 | 2543 | # check if running from terminal 2544 | t = False 2545 | ti = False 2546 | try: 2547 | t = '-t' in c 2548 | ti = '-i' in c 2549 | 2550 | except Exception as e: 2551 | pass 2552 | 2553 | 2554 | # start with terminal commands 2555 | if t or ti: 2556 | 2557 | # error bool 2558 | err = False 2559 | 2560 | # get path 2561 | WIU_AppPath = c[0] 2562 | 2563 | # nukescript 2564 | aNK = '' 2565 | for index, arg in enumerate(c): 2566 | if arg in ['-nk'] and len(c) > index + 1: 2567 | aNK = c[index + 1] 2568 | del c[index] 2569 | del c[index] 2570 | break 2571 | 2572 | # destination path 2573 | aOut = '' 2574 | for index, arg in enumerate(c): 2575 | if arg in ['-o'] and len(c) > index + 1: 2576 | aOut = c[index + 1] 2577 | del c[index] 2578 | del c[index] 2579 | break 2580 | 2581 | # start (instead of only returning a to-do list) 2582 | aStart = False 2583 | for index, arg in enumerate(c): 2584 | if arg in ['-s']: 2585 | aStart = True 2586 | del c[index] 2587 | break 2588 | 2589 | # node name directory 2590 | aNodeName = True 2591 | for index, arg in enumerate(c): 2592 | if arg in ['-n']: 2593 | aNodeName = False 2594 | del c[index] 2595 | break 2596 | 2597 | # parent directory count 2598 | aDirCount = 3 2599 | for index, arg in enumerate(c): 2600 | if arg in ['-pd']: 2601 | try: 2602 | aDirCount = int(c[index + 1]) 2603 | except Exception as e: 2604 | err = True 2605 | print(WIU_Log + 'Non-numerical value entered for -pd.') 2606 | del c[index] 2607 | del c[index] 2608 | break 2609 | 2610 | # relinked 2611 | aReli = True 2612 | for index, arg in enumerate(c): 2613 | if arg in ['-r']: 2614 | aReli = False 2615 | del c[index] 2616 | break 2617 | 2618 | # relinked relative 2619 | aReliRela = True 2620 | for index, arg in enumerate(c): 2621 | if arg in ['-rr']: 2622 | aReliRela = False 2623 | del c[index] 2624 | break 2625 | 2626 | # media 2627 | aMedia = True 2628 | for index, arg in enumerate(c): 2629 | if arg in ['-m']: 2630 | aMedia = False 2631 | del c[index] 2632 | break 2633 | 2634 | # skip disabled nodes 2635 | aSkipDisabledNodes = False 2636 | for index, arg in enumerate(c): 2637 | if arg in ['-d']: 2638 | aSkipDisabledNodes = True 2639 | del c[index] 2640 | break 2641 | 2642 | # fonts 2643 | aFonts = True 2644 | for index, arg in enumerate(c): 2645 | if arg in ['-f']: 2646 | aFonts = False 2647 | del c[index] 2648 | break 2649 | 2650 | # gizmos 2651 | aGizmos = True 2652 | for index, arg in enumerate(c): 2653 | if arg in ['-g']: 2654 | aGizmos = False 2655 | del c[index] 2656 | break 2657 | 2658 | # csv commas 2659 | aCSV = False 2660 | for index, arg in enumerate(c): 2661 | if arg in ['-csvcommas']: 2662 | aCSV = True 2663 | del c[index] 2664 | break 2665 | 2666 | # use same license 2667 | aLicInteractive = ti 2668 | 2669 | 2670 | # error handling 2671 | if err: 2672 | print(WIU_Log + 'Usage:\n-nk (required)\n-o (required)\n-s (start now - if not, only a preview list of the files to be processed will be returned)\n-n (disable: place media in node name folder)\n-pd (default: 3)\n-r (disable: make relinked .nk)\n-rr (disable: make relative relinked .nk)\n-m (disable: collect media)\n-f (disable: collect font folder)\n-g (disable: collect gizmos)\n-csvcomma (use commas instead of semicolons as the CSV separator)') 2673 | else: 2674 | WrapItUp(fromterminal = True, nk = aNK, startnow = aStart, out = aOut, nodenamefolder = aNodeName, parentdircount = aDirCount, relinked = aReli, relativerelinked = aReliRela, media = aMedia, skipdisablednodes = aSkipDisabledNodes, fonts = aFonts, gizmos = aGizmos, csvcommas = aCSV, licinteractive = aLicInteractive) 2675 | 2676 | 2677 | else: 2678 | WrapItUp() -------------------------------------------------------------------------------- /WrapItUp 汉化 Chinese Translations/menu.py: -------------------------------------------------------------------------------- 1 | import WrapItUp 2 | nuke.menu('Nuke').addCommand('Extra/Wrap It Up', "WrapItUp.WrapItUp()") -------------------------------------------------------------------------------- /WrapItUp/WrapItUp.py: -------------------------------------------------------------------------------- 1 | # Max van Leeuwen - maxvanleeuwen.com/WrapItUp 2 | # WrapItUp - 2.1 3 | # 4 | # Collect all media, gizmos and files associated with a nuke script, and copy it all to a separate folder - along with a relinked duplicate of the nuke script. 5 | 6 | 7 | 8 | WIU_Title = 'WrapItUp 2.1 - maxvanleeuwen.com' 9 | WIU_Log = '[WrapItUp] ' 10 | 11 | 12 | 13 | # import PySide(2) 14 | try: 15 | 16 | # try importing PySide2 17 | try: 18 | import PySide2.QtCore as QtCore 19 | import PySide2.QtGui as QtGui 20 | import PySide2.QtWidgets as QtWidgets 21 | 22 | # on error, try PySide (with QtGui imported as QtWidgets) 23 | except Exception as e: 24 | import PySide.QtCore as QtCore 25 | import PySide.QtGui as QtGui 26 | import PySide.QtGui as QtWidgets 27 | 28 | 29 | # ignore if in python shell 30 | except Exception as e: 31 | pass 32 | 33 | 34 | 35 | # EMBEDDED UI 36 | 37 | 38 | class Ui_Dialog(object): 39 | def setupUi(self, Dialog): 40 | Dialog.setObjectName("Dialog") 41 | Dialog.setWindowModality(QtCore.Qt.ApplicationModal) 42 | Dialog.setEnabled(True) 43 | Dialog.resize(897, 868) 44 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 45 | sizePolicy.setHorizontalStretch(0) 46 | sizePolicy.setVerticalStretch(0) 47 | sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) 48 | Dialog.setSizePolicy(sizePolicy) 49 | Dialog.setFocusPolicy(QtCore.Qt.StrongFocus) 50 | Dialog.setWindowOpacity(1.0) 51 | Dialog.setAutoFillBackground(False) 52 | Dialog.setWindowFilePath("") 53 | Dialog.setSizeGripEnabled(False) 54 | Dialog.setModal(False) 55 | self.ListCopyPaths = QtWidgets.QListWidget(Dialog) 56 | self.ListCopyPaths.setGeometry(QtCore.QRect(20, 240, 421, 411)) 57 | self.ListCopyPaths.setAutoFillBackground(False) 58 | self.ListCopyPaths.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 59 | self.ListCopyPaths.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 60 | self.ListCopyPaths.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 61 | self.ListCopyPaths.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 62 | self.ListCopyPaths.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 63 | self.ListCopyPaths.setResizeMode(QtWidgets.QListView.Fixed) 64 | self.ListCopyPaths.setObjectName("ListCopyPaths") 65 | self.ListIgnorePaths = QtWidgets.QListWidget(Dialog) 66 | self.ListIgnorePaths.setGeometry(QtCore.QRect(460, 240, 421, 411)) 67 | self.ListIgnorePaths.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 68 | self.ListIgnorePaths.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 69 | self.ListIgnorePaths.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 70 | self.ListIgnorePaths.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 71 | self.ListIgnorePaths.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 72 | self.ListIgnorePaths.setObjectName("ListIgnorePaths") 73 | self.SendToIgnore = QtWidgets.QPushButton(Dialog) 74 | self.SendToIgnore.setGeometry(QtCore.QRect(290, 660, 151, 23)) 75 | self.SendToIgnore.setAutoDefault(False) 76 | self.SendToIgnore.setObjectName("SendToIgnore") 77 | self.SendToCopy = QtWidgets.QPushButton(Dialog) 78 | self.SendToCopy.setGeometry(QtCore.QRect(460, 660, 151, 23)) 79 | self.SendToCopy.setAutoDefault(False) 80 | self.SendToCopy.setObjectName("SendToCopy") 81 | self.PackedPath = QtWidgets.QLineEdit(Dialog) 82 | self.PackedPath.setGeometry(QtCore.QRect(20, 40, 781, 20)) 83 | self.PackedPath.setText("") 84 | self.PackedPath.setObjectName("PackedPath") 85 | self.ChoosePackedPathButton = QtWidgets.QPushButton(Dialog) 86 | self.ChoosePackedPathButton.setGeometry(QtCore.QRect(810, 39, 75, 23)) 87 | self.ChoosePackedPathButton.setAutoDefault(False) 88 | self.ChoosePackedPathButton.setObjectName("ChoosePackedPathButton") 89 | self.LCopy = QtWidgets.QLabel(Dialog) 90 | self.LCopy.setGeometry(QtCore.QRect(30, 220, 47, 13)) 91 | self.LCopy.setObjectName("LCopy") 92 | self.LIgnore = QtWidgets.QLabel(Dialog) 93 | self.LIgnore.setGeometry(QtCore.QRect(470, 220, 47, 13)) 94 | self.LIgnore.setObjectName("LIgnore") 95 | self.LCurrItemItemPath = QtWidgets.QLabel(Dialog) 96 | self.LCurrItemItemPath.setGeometry(QtCore.QRect(20, 720, 131, 16)) 97 | font = QtGui.QFont() 98 | font.setBold(True) 99 | font.setWeight(75) 100 | self.LCurrItemItemPath.setFont(font) 101 | self.LCurrItemItemPath.setObjectName("LCurrItemItemPath") 102 | self.LPackedItemPath = QtWidgets.QLabel(Dialog) 103 | self.LPackedItemPath.setGeometry(QtCore.QRect(20, 740, 131, 16)) 104 | font = QtGui.QFont() 105 | font.setBold(True) 106 | font.setWeight(75) 107 | self.LPackedItemPath.setFont(font) 108 | self.LPackedItemPath.setObjectName("LPackedItemPath") 109 | self.CurrItemPath = QtWidgets.QLabel(Dialog) 110 | self.CurrItemPath.setGeometry(QtCore.QRect(150, 720, 771, 16)) 111 | self.CurrItemPath.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) 112 | self.CurrItemPath.setObjectName("CurrItemPath") 113 | self.PackedItemPath = QtWidgets.QLabel(Dialog) 114 | self.PackedItemPath.setGeometry(QtCore.QRect(150, 740, 771, 16)) 115 | self.PackedItemPath.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) 116 | self.PackedItemPath.setObjectName("PackedItemPath") 117 | self.LWebsite = QtWidgets.QLabel(Dialog) 118 | self.LWebsite.setGeometry(QtCore.QRect(20, 830, 221, 16)) 119 | self.LWebsite.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 120 | self.LWebsite.setOpenExternalLinks(True) 121 | self.LWebsite.setObjectName("LWebsite") 122 | self.LPath = QtWidgets.QLabel(Dialog) 123 | self.LPath.setGeometry(QtCore.QRect(20, 20, 781, 16)) 124 | self.LPath.setObjectName("LPath") 125 | self.LFiles = QtWidgets.QLabel(Dialog) 126 | self.LFiles.setGeometry(QtCore.QRect(20, 760, 131, 16)) 127 | font = QtGui.QFont() 128 | font.setBold(True) 129 | font.setWeight(75) 130 | self.LFiles.setFont(font) 131 | self.LFiles.setObjectName("LFiles") 132 | self.CurrItemFiles = QtWidgets.QLabel(Dialog) 133 | self.CurrItemFiles.setGeometry(QtCore.QRect(150, 760, 771, 16)) 134 | self.CurrItemFiles.setObjectName("CurrItemFiles") 135 | self.TotalProgress = QtWidgets.QProgressBar(Dialog) 136 | self.TotalProgress.setEnabled(True) 137 | self.TotalProgress.setGeometry(QtCore.QRect(370, 830, 161, 23)) 138 | self.TotalProgress.setProperty("value", 0) 139 | self.TotalProgress.setTextVisible(True) 140 | self.TotalProgress.setOrientation(QtCore.Qt.Horizontal) 141 | self.TotalProgress.setInvertedAppearance(False) 142 | self.TotalProgress.setTextDirection(QtWidgets.QProgressBar.TopToBottom) 143 | self.TotalProgress.setObjectName("TotalProgress") 144 | self.Refresh = QtWidgets.QPushButton(Dialog) 145 | self.Refresh.setGeometry(QtCore.QRect(800, 660, 75, 23)) 146 | self.Refresh.setAutoDefault(False) 147 | self.Refresh.setObjectName("Refresh") 148 | self.Start = QtWidgets.QPushButton(Dialog) 149 | self.Start.setGeometry(QtCore.QRect(710, 830, 75, 23)) 150 | self.Start.setAutoDefault(False) 151 | self.Start.setObjectName("Start") 152 | self.Interrupt = QtWidgets.QPushButton(Dialog) 153 | self.Interrupt.setGeometry(QtCore.QRect(800, 830, 75, 23)) 154 | self.Interrupt.setAutoDefault(False) 155 | self.Interrupt.setObjectName("Interrupt") 156 | self.LSize = QtWidgets.QLabel(Dialog) 157 | self.LSize.setGeometry(QtCore.QRect(20, 780, 131, 16)) 158 | font = QtGui.QFont() 159 | font.setBold(True) 160 | font.setWeight(75) 161 | self.LSize.setFont(font) 162 | self.LSize.setObjectName("LSize") 163 | self.CurrItemSize = QtWidgets.QLabel(Dialog) 164 | self.CurrItemSize.setGeometry(QtCore.QRect(150, 780, 771, 16)) 165 | self.CurrItemSize.setObjectName("CurrItemSize") 166 | self.LTotalCopySize = QtWidgets.QLabel(Dialog) 167 | self.LTotalCopySize.setGeometry(QtCore.QRect(560, 830, 71, 21)) 168 | font = QtGui.QFont() 169 | font.setBold(True) 170 | font.setWeight(75) 171 | self.LTotalCopySize.setFont(font) 172 | self.LTotalCopySize.setObjectName("LTotalCopySize") 173 | self.TotalCopySize = QtWidgets.QLabel(Dialog) 174 | self.TotalCopySize.setGeometry(QtCore.QRect(640, 830, 71, 21)) 175 | self.TotalCopySize.setObjectName("TotalCopySize") 176 | self.IgnoredLabel = QtWidgets.QLabel(Dialog) 177 | self.IgnoredLabel.setGeometry(QtCore.QRect(20, 700, 131, 16)) 178 | font = QtGui.QFont() 179 | font.setBold(True) 180 | font.setWeight(75) 181 | self.IgnoredLabel.setFont(font) 182 | self.IgnoredLabel.setToolTip("") 183 | self.IgnoredLabel.setObjectName("IgnoredLabel") 184 | self.CurrentCopyItem = QtWidgets.QLabel(Dialog) 185 | self.CurrentCopyItem.setGeometry(QtCore.QRect(370, 800, 161, 21)) 186 | self.CurrentCopyItem.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) 187 | self.CurrentCopyItem.setObjectName("CurrentCopyItem") 188 | self.GoToFolder = QtWidgets.QPushButton(Dialog) 189 | self.GoToFolder.setEnabled(False) 190 | self.GoToFolder.setGeometry(QtCore.QRect(20, 660, 75, 23)) 191 | self.GoToFolder.setAutoDefault(False) 192 | self.GoToFolder.setObjectName("GoToFolder") 193 | self.ItemProgress = QtWidgets.QProgressBar(Dialog) 194 | self.ItemProgress.setEnabled(True) 195 | self.ItemProgress.setGeometry(QtCore.QRect(370, 800, 161, 23)) 196 | self.ItemProgress.setProperty("value", 0) 197 | self.ItemProgress.setTextVisible(False) 198 | self.ItemProgress.setTextDirection(QtWidgets.QProgressBar.TopToBottom) 199 | self.ItemProgress.setObjectName("ItemProgress") 200 | self.SettingPages = QtWidgets.QTabWidget(Dialog) 201 | self.SettingPages.setGeometry(QtCore.QRect(20, 90, 861, 111)) 202 | self.SettingPages.setObjectName("SettingPages") 203 | self.MainSettings = QtWidgets.QWidget() 204 | self.MainSettings.setObjectName("MainSettings") 205 | self.RelinkPaths = QtWidgets.QCheckBox(self.MainSettings) 206 | self.RelinkPaths.setGeometry(QtCore.QRect(20, 20, 271, 17)) 207 | self.RelinkPaths.setCheckable(True) 208 | self.RelinkPaths.setChecked(True) 209 | self.RelinkPaths.setObjectName("RelinkPaths") 210 | self.RelativeRelink = QtWidgets.QCheckBox(self.MainSettings) 211 | self.RelativeRelink.setEnabled(True) 212 | self.RelativeRelink.setGeometry(QtCore.QRect(20, 40, 321, 17)) 213 | self.RelativeRelink.setChecked(True) 214 | self.RelativeRelink.setObjectName("RelativeRelink") 215 | self.LParentDirectories = QtWidgets.QLabel(self.MainSettings) 216 | self.LParentDirectories.setGeometry(QtCore.QRect(510, 40, 191, 21)) 217 | self.LParentDirectories.setObjectName("LParentDirectories") 218 | self.ParentDirectories = QtWidgets.QSpinBox(self.MainSettings) 219 | self.ParentDirectories.setGeometry(QtCore.QRect(460, 40, 41, 21)) 220 | self.ParentDirectories.setSuffix("") 221 | self.ParentDirectories.setPrefix("") 222 | self.ParentDirectories.setMinimum(1) 223 | self.ParentDirectories.setMaximum(99) 224 | self.ParentDirectories.setProperty("value", 3) 225 | self.ParentDirectories.setObjectName("ParentDirectories") 226 | self.NodeNameFolder = QtWidgets.QCheckBox(self.MainSettings) 227 | self.NodeNameFolder.setGeometry(QtCore.QRect(460, 20, 271, 17)) 228 | self.NodeNameFolder.setChecked(True) 229 | self.NodeNameFolder.setObjectName("NodeNameFolder") 230 | self.SettingPages.addTab(self.MainSettings, "") 231 | self.tab = QtWidgets.QWidget() 232 | self.tab.setObjectName("tab") 233 | self.CopyFontDir = QtWidgets.QCheckBox(self.tab) 234 | self.CopyFontDir.setGeometry(QtCore.QRect(20, 20, 271, 17)) 235 | self.CopyFontDir.setChecked(True) 236 | self.CopyFontDir.setObjectName("CopyFontDir") 237 | self.CopyGizmos = QtWidgets.QCheckBox(self.tab) 238 | self.CopyGizmos.setGeometry(QtCore.QRect(20, 40, 271, 17)) 239 | self.CopyGizmos.setChecked(True) 240 | self.CopyGizmos.setObjectName("CopyGizmos") 241 | self.SettingPages.addTab(self.tab, "") 242 | self.Misc = QtWidgets.QWidget() 243 | self.Misc.setObjectName("Misc") 244 | self.ContinueOnError = QtWidgets.QCheckBox(self.Misc) 245 | self.ContinueOnError.setGeometry(QtCore.QRect(20, 20, 171, 17)) 246 | self.ContinueOnError.setChecked(True) 247 | self.ContinueOnError.setTristate(False) 248 | self.ContinueOnError.setObjectName("ContinueOnError") 249 | self.ExitOnFinish = QtWidgets.QCheckBox(self.Misc) 250 | self.ExitOnFinish.setGeometry(QtCore.QRect(20, 40, 171, 17)) 251 | self.ExitOnFinish.setChecked(False) 252 | self.ExitOnFinish.setTristate(False) 253 | self.ExitOnFinish.setObjectName("ExitOnFinish") 254 | self.CSVSeparator = QtWidgets.QComboBox(self.Misc) 255 | self.CSVSeparator.setGeometry(QtCore.QRect(460, 18, 41, 22)) 256 | self.CSVSeparator.setObjectName("CSVSeparator") 257 | self.CSVSeparator.addItem("") 258 | self.CSVSeparator.addItem("") 259 | self.LCSVSeparator = QtWidgets.QLabel(self.Misc) 260 | self.LCSVSeparator.setGeometry(QtCore.QRect(510, 18, 191, 21)) 261 | self.LCSVSeparator.setObjectName("LCSVSeparator") 262 | self.License = QtWidgets.QComboBox(self.Misc) 263 | self.License.setGeometry(QtCore.QRect(460, 38, 41, 22)) 264 | self.License.setObjectName("License") 265 | self.License.addItem("") 266 | self.License.addItem("") 267 | self.LLicense = QtWidgets.QLabel(self.Misc) 268 | self.LLicense.setGeometry(QtCore.QRect(510, 38, 191, 21)) 269 | self.LLicense.setObjectName("LLicense") 270 | self.SettingPages.addTab(self.Misc, "") 271 | self.GoToRootFolder = QtWidgets.QPushButton(Dialog) 272 | self.GoToRootFolder.setGeometry(QtCore.QRect(810, 70, 75, 23)) 273 | self.GoToRootFolder.setAutoDefault(False) 274 | self.GoToRootFolder.setObjectName("GoToRootFolder") 275 | self.GoToNode = QtWidgets.QPushButton(Dialog) 276 | self.GoToNode.setEnabled(False) 277 | self.GoToNode.setGeometry(QtCore.QRect(100, 660, 75, 23)) 278 | self.GoToNode.setAutoDefault(False) 279 | self.GoToNode.setObjectName("GoToNode") 280 | self.ItemProgress.raise_() 281 | self.ListCopyPaths.raise_() 282 | self.ListIgnorePaths.raise_() 283 | self.SendToIgnore.raise_() 284 | self.SendToCopy.raise_() 285 | self.PackedPath.raise_() 286 | self.ChoosePackedPathButton.raise_() 287 | self.LCopy.raise_() 288 | self.LIgnore.raise_() 289 | self.LCurrItemItemPath.raise_() 290 | self.LPackedItemPath.raise_() 291 | self.CurrItemPath.raise_() 292 | self.PackedItemPath.raise_() 293 | self.LWebsite.raise_() 294 | self.LPath.raise_() 295 | self.LFiles.raise_() 296 | self.CurrItemFiles.raise_() 297 | self.TotalProgress.raise_() 298 | self.Refresh.raise_() 299 | self.Start.raise_() 300 | self.Interrupt.raise_() 301 | self.LSize.raise_() 302 | self.CurrItemSize.raise_() 303 | self.LTotalCopySize.raise_() 304 | self.TotalCopySize.raise_() 305 | self.IgnoredLabel.raise_() 306 | self.CurrentCopyItem.raise_() 307 | self.GoToFolder.raise_() 308 | self.SettingPages.raise_() 309 | self.GoToRootFolder.raise_() 310 | self.GoToNode.raise_() 311 | 312 | self.retranslateUi(Dialog) 313 | self.SettingPages.setCurrentIndex(0) 314 | self.CSVSeparator.setCurrentIndex(0) 315 | self.License.setCurrentIndex(0) 316 | QtCore.QMetaObject.connectSlotsByName(Dialog) 317 | 318 | def retranslateUi(self, Dialog): 319 | _translate = QtCore.QCoreApplication.translate 320 | Dialog.setWindowTitle(_translate("Dialog", "WrapItUp - Max van Leeuwen")) 321 | self.ListCopyPaths.setToolTip(_translate("Dialog", "

The found items that will be copied to the specified collection path.

An asterisk (*) in front of a media path means that all of its nodes are disabled.

")) 322 | self.ListIgnorePaths.setToolTip(_translate("Dialog", "

The found items that will not be copied to the specified collection path.

")) 323 | self.SendToIgnore.setToolTip(_translate("Dialog", "Ignore selected items on the left.")) 324 | self.SendToIgnore.setText(_translate("Dialog", ">>")) 325 | self.SendToCopy.setToolTip(_translate("Dialog", "Stop ignoring selected items on the right.")) 326 | self.SendToCopy.setText(_translate("Dialog", "<<")) 327 | self.PackedPath.setToolTip(_translate("Dialog", "

The location of the folder to collect all data to.
Make sure this is an empty folder.

")) 328 | self.ChoosePackedPathButton.setToolTip(_translate("Dialog", "

The location of the folder to collect all data to.
Make sure this is an empty folder.

")) 329 | self.ChoosePackedPathButton.setText(_translate("Dialog", "folder")) 330 | self.LCopy.setText(_translate("Dialog", "copy")) 331 | self.LIgnore.setText(_translate("Dialog", "ignore")) 332 | self.LCurrItemItemPath.setToolTip(_translate("Dialog", "Current path of the selected item.")) 333 | self.LCurrItemItemPath.setText(_translate("Dialog", "current item path")) 334 | self.LPackedItemPath.setToolTip(_translate("Dialog", "

Preview of the selected item\'s new path - after it is collected.

")) 335 | self.LPackedItemPath.setText(_translate("Dialog", "packed item path")) 336 | self.CurrItemPath.setText(_translate("Dialog", "-")) 337 | self.PackedItemPath.setText(_translate("Dialog", "-")) 338 | self.LWebsite.setToolTip(_translate("Dialog", "My website!")) 339 | self.LWebsite.setText(_translate("Dialog", "

maxvanleeuwen.com/WrapItUp

")) 340 | self.LPath.setToolTip(_translate("Dialog", "

The location of the folder to collect all data to.
Make sure this is an empty folder.

")) 341 | self.LPath.setText(_translate("Dialog", "collection folder:")) 342 | self.LFiles.setToolTip(_translate("Dialog", "

File/frame count of the selected item.

")) 343 | self.LFiles.setText(_translate("Dialog", "files")) 344 | self.CurrItemFiles.setText(_translate("Dialog", "-")) 345 | self.TotalProgress.setToolTip(_translate("Dialog", "

Total progress.

")) 346 | self.Refresh.setToolTip(_translate("Dialog", "

Recheck the current Nuke script for media and other files to collect.

")) 347 | self.Refresh.setText(_translate("Dialog", "refresh")) 348 | self.Start.setToolTip(_translate("Dialog", "

Start with the current settings.
One final confirmation will be shown.

")) 349 | self.Start.setText(_translate("Dialog", "start...")) 350 | self.Interrupt.setToolTip(_translate("Dialog", "

Stop/exit.

")) 351 | self.Interrupt.setText(_translate("Dialog", "interrupt")) 352 | self.LSize.setToolTip(_translate("Dialog", "

Size of the selected item.

")) 353 | self.LSize.setText(_translate("Dialog", "size")) 354 | self.CurrItemSize.setText(_translate("Dialog", "-")) 355 | self.LTotalCopySize.setToolTip(_translate("Dialog", "Total size of all files to be copied.")) 356 | self.LTotalCopySize.setText(_translate("Dialog", "total size")) 357 | self.TotalCopySize.setToolTip(_translate("Dialog", "Total size of all files to be copied.")) 358 | self.TotalCopySize.setText(_translate("Dialog", "0")) 359 | self.IgnoredLabel.setText(_translate("Dialog", "ignored!")) 360 | self.CurrentCopyItem.setText(_translate("Dialog", "item loading...")) 361 | self.GoToFolder.setToolTip(_translate("Dialog", "

Go to the folder of the currently selected item.

alt/option+shift+r

")) 362 | self.GoToFolder.setText(_translate("Dialog", "open folder")) 363 | self.GoToFolder.setShortcut(_translate("Dialog", "Alt+Shift+R")) 364 | self.ItemProgress.setToolTip(_translate("Dialog", "

Item progress.

")) 365 | self.RelinkPaths.setToolTip(_translate("Dialog", "

Make another copy of the Nuke script in which all nodes that have their media copied over will be relinked.

")) 366 | self.RelinkPaths.setText(_translate("Dialog", "make nuke script copy, relinked")) 367 | self.RelativeRelink.setToolTip(_translate("Dialog", "

Make another copy of the Nuke script in which all nodes that have their media copied over will be relinked using the following path prefix: [python {nuke.script_directory()}]

This way, the nuke script will keep working, even if it has been moved to a different location/machine - as long as the media files are right next to it.

")) 368 | self.RelativeRelink.setText(_translate("Dialog", "make nuke script copy, relative relinked")) 369 | self.LParentDirectories.setToolTip(_translate("Dialog", "

The amount of (empty) parent directories to copy over for each found media item.

For instance:
An image sequence in /path/to/image/files/file_####.exr with a parent directory count of 3 will have the following destination in the final collected path:

../MEDIA/image/files/file_####.exr


If the \'place media in folder with node name\' checkbox is unchecked, do not make this number too small.
Fiiles with same-name parent directories might end up overwriting, or merging together.

")) 370 | self.LParentDirectories.setText(_translate("Dialog", "parent directories")) 371 | self.ParentDirectories.setToolTip(_translate("Dialog", "

The amount of (empty) parent directories to copy over for each found media item.

For instance:
An image sequence in /path/to/image/files/file_####.exr with a parent directory count of 3 will have the following destination in the final collected path:

../MEDIA/image/files/file_####.exr


If the \'place media in folder with node name\' checkbox is unchecked, do not make this number too small.
Fiiles with same-name parent directories might end up overwriting, or merging together.

")) 372 | self.NodeNameFolder.setToolTip(_translate("Dialog", "

Place all media items in a subfolder with its corresponding node as that folder\'s name.

This helps finding which media belonged to which node in the new comp, and it is an extra measure against the kind of problems that could arise when parent folders of different media items have the same names.

")) 373 | self.NodeNameFolder.setText(_translate("Dialog", "place media in folder with node name")) 374 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.MainSettings), _translate("Dialog", "media")) 375 | self.CopyFontDir.setToolTip(_translate("Dialog", "

If the current Nuke script has a custom Project Font Path set in Settings > Node, collect this directory.

")) 376 | self.CopyFontDir.setText(_translate("Dialog", "copy font directory (if any)")) 377 | self.CopyGizmos.setToolTip(_translate("Dialog", "

If custom gizmos are used in this Nuke script, collect them and generate an init.py and menu.py file that installs them on a different machine.

This function does not work for all gizmos, as they can be dependent on other files.
This does not work for plugins (.dll, .so, .dylib).

")) 378 | self.CopyGizmos.setText(_translate("Dialog", "copy gizmos")) 379 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.tab), _translate("Dialog", "add-ons")) 380 | self.ContinueOnError.setToolTip(_translate("Dialog", "

Continue copying if an error occurs.
If there are errors, check the log.txt file generated in the selected root folder.

")) 381 | self.ContinueOnError.setText(_translate("Dialog", "continue on error")) 382 | self.ExitOnFinish.setToolTip(_translate("Dialog", "

Close Nuke entirely when the copying process is finished (or on error, if \'continue on error\' is disabled).

Useful for machines that are licensed using a limited number of floating licenses on a license server.

")) 383 | self.ExitOnFinish.setText(_translate("Dialog", "exit nuke on finish")) 384 | self.CSVSeparator.setToolTip(_translate("Dialog", "

Set the log file\'s CSV column separator.

")) 385 | self.CSVSeparator.setItemText(0, _translate("Dialog", ";")) 386 | self.CSVSeparator.setItemText(1, _translate("Dialog", ",")) 387 | self.LCSVSeparator.setToolTip(_translate("Dialog", "

Set the log file\'s CSV column separator.

")) 388 | self.LCSVSeparator.setText(_translate("Dialog", "CSV separator")) 389 | self.SettingPages.setTabText(self.SettingPages.indexOf(self.Misc), _translate("Dialog", "misc")) 390 | self.License.setToolTip(_translate("Dialog", "

Use this flag internally when running Nuke in command-line (when relinking).
The -t flag uses a nuke_r license, the -ti flag uses a nuke_i license.

")) 391 | self.License.setItemText(0, _translate("Dialog", "-t")) 392 | self.License.setItemText(1, _translate("Dialog", "-ti")) 393 | self.LLicense.setToolTip(_translate("Dialog", "

Use this flag internally when running Nuke in command-line (when relinking).
The -t flag uses a nuke_r license, the -ti flag uses a nuke_i license.

")) 394 | self.LLicense.setText(_translate("Dialog", "relink license")) 395 | self.GoToRootFolder.setToolTip(_translate("Dialog", "

Open the currently selected folder.

")) 396 | self.GoToRootFolder.setText(_translate("Dialog", "open folder")) 397 | self.GoToNode.setToolTip(_translate("Dialog", "

Go to the folder of the currently selected item.

alt/option+shift+r

")) 398 | self.GoToNode.setText(_translate("Dialog", "go to nodes")) 399 | 400 | 401 | # END OF EMBEDDED UI 402 | 403 | 404 | 405 | # import necessary 406 | import sys 407 | import nuke 408 | import shutil 409 | import threading 410 | import os 411 | import glob 412 | import re 413 | import subprocess 414 | import webbrowser 415 | import time 416 | 417 | 418 | 419 | # globals 420 | WIU_PackedPath = '' 421 | WIU_Interrupted = False 422 | WIU_TotalSize = 0 423 | WIU_Copying = False 424 | WIU_ProjectDir = False 425 | 426 | WIU_Relink = True 427 | WIU_RelinkRelative = True 428 | WIU_Gizmo = True 429 | WIU_Fonts = True 430 | WIU_ParentDirCount = 3 431 | WIU_NodeNameFolder = True 432 | WIU_AppPath = '' 433 | 434 | WIU_SilentReturn = [] 435 | WIU_SilentList = [] 436 | 437 | WIU_MediaDataNotIgnored = [] 438 | 439 | 440 | 441 | # bytes to readable string 442 | def BytesToString(B): 443 | B = float(B) 444 | KB = float(1024) 445 | MB = float(KB ** 2) # 1.048.576 446 | GB = float(KB ** 3) # 1.073.741.824 447 | TB = float(KB ** 4) # 1.099.511.627.776 448 | 449 | if B < KB: 450 | return '{0} {1}'.format(B, 'B') 451 | elif KB <= B < MB: 452 | return '{0:.2f} KB'.format(B/KB) 453 | elif MB <= B < GB: 454 | return '{0:.2f} MB'.format(B/MB) 455 | elif GB <= B < TB: 456 | return '{0:.2f} GB'.format(B/GB) 457 | elif TB <= B: 458 | return '{0:.2f} TB'.format(B/TB) 459 | 460 | 461 | 462 | def evalTCL(text): 463 | 464 | val = '' 465 | try: 466 | val = nuke.tcl("[return \"" + text + "\"]") 467 | except Exception as e: 468 | # TCL not working for this string 469 | pass 470 | 471 | # only allow string type to be returned 472 | if type(val) is not str: 473 | val = text 474 | 475 | return val 476 | 477 | 478 | 479 | 480 | def _Start(silent = False, nk = '', startnow = False, out = '', nodenamefolder = True, parentdircount = 3, relinked = True, relativerelinked = True, media = True, skipdisablednodes = False, fonts = True, gizmos = True, csvcommas = False, licinteractive = False): 481 | 482 | # reset list 483 | global WIU_SilentList 484 | WIU_SilentList = [] 485 | 486 | 487 | # add project directory to start of path (or get project dir path) 488 | def ProjectDirectory(pth = ''): 489 | 490 | # set global projectdir 491 | global WIU_ProjectDir 492 | WIU_ProjectDir = True 493 | 494 | # get project directory 495 | projectdir = nuke.root()['project_directory'].getValue() 496 | 497 | # remove double slash 498 | if projectdir.endswith('/') and pth.startswith('/'): 499 | projectdir = projectdir[:-1] 500 | 501 | # stitch together 502 | pth = projectdir + pth 503 | 504 | return pth 505 | 506 | 507 | # returns all paths one file knob could refer to 508 | def GetRealKnobPaths(knobPath): 509 | 510 | # container for all found paths in sequence 511 | paths = [] 512 | 513 | # all versions of knobPath (stereo views, %v) 514 | viewFiles = [] 515 | 516 | # project directory relative path 517 | projectdir = False 518 | 519 | # check if stereo files are used 520 | if r'%v' in knobPath or r'%V' in knobPath: 521 | 522 | # get all stereo views in the comp 523 | viewRtn = nuke.root().knob('views').toScript() 524 | 525 | for rtn in viewRtn.split('\n'): 526 | 527 | # get each view name in the nuke comp 528 | view = rtn.split(' ')[-2] 529 | 530 | # replace in path and append to viewfiles 531 | viewFiles.append(knobPath.replace(r'%v', view).replace(r'%V', view)) 532 | 533 | # if not, do not replace anything 534 | else: 535 | viewFiles = [knobPath] 536 | 537 | 538 | # overwrite knobPath value with new value per view 539 | for knobPath in viewFiles: 540 | 541 | # get TCL evaluated string 542 | knobPathTCL = evalTCL(knobPath) 543 | 544 | # get parent directory 545 | knobPathParentDir = os.path.dirname(knobPathTCL) 546 | 547 | 548 | # try appending project root folder, if the dir does not exist 549 | if not os.path.exists(knobPathParentDir): 550 | 551 | knobPath_projectdir = ProjectDirectory(knobPathParentDir) 552 | 553 | if os.path.isdir(knobPath_projectdir): 554 | projectdir = True 555 | knobPathParentDir = knobPath_projectdir 556 | knobPathTCL = ProjectDirectory(knobPathTCL) 557 | 558 | 559 | 560 | 561 | # check if the parent dir exists 562 | if os.path.exists(knobPathParentDir): 563 | 564 | # if it does, get the filename 565 | filename = knobPathTCL.split('/')[-1] 566 | 567 | # get number from printf notation as int 568 | printfCount = -1 569 | try: 570 | # regex pattern for printf notation (only get first result of found printf notations) 571 | regex = re.compile('%..d') 572 | regexFile = regex.findall(filename)[0] 573 | printfCount = int(regexFile[1:-1]) 574 | except Exception as e: 575 | # no printf used 576 | pass 577 | 578 | 579 | # if printf notation is used for the sequence 580 | if printfCount > 0: 581 | 582 | # make wildcard string (e.g. '????') for glob with same length as # 583 | wildcards = '' 584 | for i in range(printfCount): 585 | wildcards += '?' 586 | 587 | wildcardPath = knobPathTCL.replace(regexFile, wildcards) 588 | 589 | # get all files in directory 590 | files = glob.glob(wildcardPath) 591 | for eachFile in files: 592 | paths.append(eachFile.replace('\\', '/')) 593 | 594 | 595 | # if hash notation is used for the sequence 596 | elif '#' in filename: 597 | 598 | # split by # 599 | filenameSplit = filename.split('#') 600 | 601 | # count amount of # 602 | wildcardCount = len(filenameSplit) - 2 603 | 604 | # make wildcard string (e.g. '????') for glob with same length as # 605 | wildcards = '' 606 | for i in range(wildcardCount + 1): 607 | wildcards += '?' 608 | 609 | # get full filename with wildcard replaced 610 | filename = filenameSplit[ -len(filenameSplit) ] + wildcards + filenameSplit[ -1 ] 611 | 612 | # full file path 613 | wildcardPath = os.path.join(knobPathParentDir, filename).replace('\\', '/') 614 | 615 | # get all files that match wildcard pattern 616 | files = glob.glob(wildcardPath) 617 | for eachFile in files: 618 | paths.append(eachFile.replace('\\', '/')) 619 | 620 | 621 | # if not a sequence 622 | else: 623 | 624 | # append this file to paths, if it exists 625 | if os.path.isfile(knobPathTCL): 626 | paths.append(knobPathTCL) 627 | 628 | # check if it is a relative (project directory) path 629 | elif os.path.isfile(ProjectDirectory(knobPathTCL)): 630 | paths.append(ProjectDirectory(knobPathTCL)) 631 | 632 | 633 | # return result 634 | return [paths, projectdir] 635 | 636 | 637 | 638 | # function to shorten a path to where it can be appended to the chosen packed media directory path 639 | def PackedPath(fullPath, i = 0, nodeName = '', fontFolder = '', project_dir = False, settingFontPath = False): 640 | 641 | global WIU_NodeNameFolder 642 | global WIU_ParentDirCount 643 | 644 | # set node name folder to argument if the UI is not loaded 645 | if silent: 646 | WIU_NodeNameFolder = nodenamefolder 647 | WIU_ParentDirCount = parentdircount 648 | 649 | # TCL eval 650 | fullPath = evalTCL(fullPath) 651 | 652 | 653 | # media (default) 654 | if i == 0: 655 | 656 | # get amount of parent directories 657 | parentDirCount = WIU_ParentDirCount 658 | 659 | # split into parent dirs 660 | splitPath = fullPath.split('/') 661 | splitCleanPath = [] 662 | for s in splitPath: 663 | if s is not '': 664 | splitCleanPath.append(s) 665 | 666 | 667 | # build shortened path (do not add / at the end of the path) 668 | newPath = (nodeName + '/') if (WIU_NodeNameFolder and not project_dir) else '' 669 | 670 | # make actual parent dir count equal to user-chosen parent dir count when it is higher, and when the current media is from project_dir 671 | if parentDirCount > len(splitCleanPath) or project_dir: 672 | parentDirCount = len(splitCleanPath) 673 | 674 | for c in range(parentDirCount): 675 | newPath += splitCleanPath[ len(splitCleanPath) - (parentDirCount - c) ] + ('/' if not c == parentDirCount - 1 else '') 676 | 677 | if project_dir: 678 | newPath = newPath.replace(ProjectDirectory(), '') 679 | 680 | 681 | # sanitise string 682 | illegalChars = ':<>$?!;\'\"\\`*|' 683 | for c in illegalChars: 684 | newPath = newPath.replace(c, '_') 685 | 686 | 687 | subdir = 'MEDIA' 688 | if project_dir: 689 | subdir = 'PROJECT_DIRECTORY' 690 | 691 | newPath = WIU_PackedPath + '/' + subdir + '/' + newPath 692 | 693 | return newPath 694 | 695 | fileName = fullPath.split('/')[-1] 696 | 697 | # nuke script 698 | if i == -1: 699 | 700 | newPath = WIU_PackedPath + '/' + fullPath.split('/')[-1] 701 | return newPath 702 | 703 | # nuke script relinked 704 | if i == -2: 705 | 706 | newPath = WIU_PackedPath + '/' + os.path.splitext( fileName )[0] + '_RELINKED.nk' 707 | return newPath 708 | 709 | # nuke script relinked relative 710 | if i == -3: 711 | 712 | newPath = WIU_PackedPath + '/' + os.path.splitext( fileName )[0] + '_RELINKED-RELATIVE.nk' 713 | return newPath 714 | 715 | # font directory 716 | if i == -4: 717 | 718 | newPath = WIU_PackedPath + '/' + 'FONTS' + '/' + fontFolder.split('/')[-1] + ( ('/' + fullPath.split('/')[-1]) if not settingFontPath else '') 719 | return newPath 720 | 721 | # gizmo 722 | if i <= -5: 723 | 724 | newPath = WIU_PackedPath + '/' + 'GIZMOS/Collected' + '/' + os.path.splitext( fileName )[0] + '/' + fileName 725 | return newPath 726 | 727 | 728 | 729 | # gets all file knobs in the comp file and runs them through GetRealKnobPaths() - returns results 730 | def ReadCompMediaData(): 731 | 732 | # progress bar total value 733 | prTotal = len(nuke.allNodes(recurseGroups=True)) 734 | 735 | # container for all loaded files 736 | readFiles = [] 737 | 738 | # collect all knobs with files in them 739 | iNode = 0 740 | for eachNode in nuke.allNodes(recurseGroups=True): 741 | if(silent and eachNode.knob('disable')): # only if a 'disable' knob exists on this node 742 | if(skipdisablednodes and eachNode['disable'].getValue()): continue # if node is disabled and 'ignore disabled nodes' flag is set (terminal only), skip this node 743 | 744 | for eachKnob in eachNode.knobs(): 745 | currKnob = eachNode[eachKnob] 746 | 747 | if currKnob.Class() == 'File_Knob': 748 | 749 | # only add if a path has been entered 750 | foundPath = currKnob.getValue() 751 | if foundPath is not '': 752 | 753 | # get real paths (file path list + project dir bool) 754 | RealKnobPathsResult = GetRealKnobPaths(foundPath) 755 | 756 | realKnobPaths = RealKnobPathsResult[0] 757 | projectdir = RealKnobPathsResult[1] 758 | 759 | # make new list for new paths with their per-file size included 760 | allFilesWithSizes = [] 761 | 762 | # get total file size 763 | totalSize = 0 764 | for eachFile in realKnobPaths: 765 | size = os.path.getsize(eachFile) 766 | totalSize += size 767 | allFilesWithSizes.append([eachFile, size]) 768 | 769 | # check if the path exists 770 | exists = False 771 | if len(realKnobPaths) > 0: 772 | exists = True 773 | 774 | # check if this node is disabled 775 | allNodesDisabled = False 776 | if(eachNode.knob('disable')): 777 | if(eachNode['disable'].getValue()): allNodesDisabled = True 778 | 779 | # make media item (node(s), knob(s), file exists, knob value, all paths and sizes per item, total size, is relative from project directory, bool if all nodes with this media are disabled) 780 | mediaItem = [[eachNode], [eachKnob], exists, foundPath, allFilesWithSizes, totalSize, projectdir, allNodesDisabled] 781 | 782 | # check if the media item has already been found via another node 783 | existingItem = None 784 | i = 0 785 | for m in readFiles: 786 | 787 | # remember item index 788 | if m[4] == mediaItem[4] and m[3] == mediaItem[3]: 789 | existingItem = i 790 | break 791 | i += 1 792 | 793 | # append node and knob to existing item instead of appending the new item 794 | if existingItem is not None: 795 | readFiles[existingItem][0].append(eachNode) 796 | readFiles[existingItem][1].append(eachKnob) 797 | if(readFiles[existingItem][7] and not allNodesDisabled): readFiles[existingItem][7] = False # if previous was disabled and this one isn't, un-disable the node 798 | 799 | # item is new 800 | else: 801 | # append all data to final list 802 | readFiles.append( mediaItem ) 803 | 804 | if not silent: 805 | nuke.executeInMainThread(TotalProgressUpdate, args=(int(round( float(iNode) / float(prTotal) * 100 / 2 )))) 806 | 807 | iNode += 1 808 | 809 | # return results 810 | return readFiles 811 | 812 | 813 | # check if a node is a gizmo, and if so, return the full name 814 | def isGizmo(n): 815 | 816 | # compare type 817 | gizmo = type(n) == nuke.Gizmo 818 | 819 | # append .gizmo 820 | gizmoName = n.Class() if n.Class().endswith('.gizmo') else n.Class() + '.gizmo' 821 | 822 | # return 823 | if gizmo: 824 | return gizmoName 825 | else: 826 | return '' 827 | 828 | 829 | 830 | # window to show when nuke should be saved first 831 | def SaveNukeFirst(): 832 | 833 | nuke.message("Save the Nuke script first!") 834 | window.close() 835 | 836 | 837 | 838 | def ReadCompOtherData(): 839 | 840 | # total progress bar value 841 | prTotal = len(nuke.allNodes(recurseGroups=True)) 842 | 843 | # all non-media data is collected here 844 | readData = [] 845 | 846 | # get current nuke script location 847 | NukeScript = '' 848 | NukeScriptSize = 0 849 | 850 | 851 | if not silent: 852 | try: 853 | NukeScript = nuke.root().name() 854 | NukeScriptSize = os.path.getsize(NukeScript) 855 | 856 | except Exception as e: 857 | nuke.executeInMainThread(SaveNukeFirst, args=()) 858 | return False 859 | else: 860 | NukeScript = nk 861 | NukeScriptSize = os.path.getsize(nk) 862 | 863 | 864 | # item 0: nukescript 865 | readData.append([NukeScript, NukeScriptSize]) 866 | 867 | # item 1: nukescript relinked 868 | readData.append([NukeScript, NukeScriptSize]) 869 | 870 | # item 2: nukescript relative relinked 871 | readData.append([NukeScript, NukeScriptSize]) 872 | 873 | # item 3: get the project font path 874 | fontPathRaw = nuke.root()['free_type_font_path'].getValue() 875 | fontPath = evalTCL(fontPathRaw) 876 | 877 | totalSize = 0 878 | 879 | # collection of all font files 880 | fontPathsS = [] 881 | for path, subdirs, files in os.walk(fontPath): 882 | for eachFile in files: 883 | currFile = os.path.join(path, eachFile).replace('\\', '/') 884 | currSize = os.path.getsize(currFile) 885 | totalSize += currSize 886 | fontPathsS.append([currFile, currSize]) 887 | 888 | # list with font path, total size of all files, list of font files + sizes 889 | readData.append([fontPath, totalSize, fontPathsS]) 890 | 891 | 892 | # item 4: get all gizmos 893 | gizmoList = [] 894 | 895 | # get each gizmo name as string 896 | for eachNode in nuke.allNodes(recurseGroups=True): 897 | 898 | # check if gizmo 899 | gizmoName = isGizmo(eachNode) 900 | 901 | # find gizmo items 902 | if gizmoName is not '': 903 | gizmoItem = [gizmoName, [eachNode]] 904 | 905 | # check if the gizmo was already found earlier, and if so, append only the node to the already found gizmo item 906 | i = 0 907 | alreadyFound = False 908 | for g in gizmoList: 909 | if g[0] == gizmoName: 910 | alreadyFound = True 911 | gizmoList[i][1].append(eachNode) 912 | i+=1 913 | 914 | 915 | if not alreadyFound: 916 | gizmoList.append( gizmoItem ) 917 | 918 | 919 | # iterate through plugin paths to search for them 920 | iNode = 0 921 | for eachPluginPath in nuke.pluginPath(): 922 | 923 | nodeI = 0 924 | for eachGizmo in gizmoList: 925 | gizmoPath = os.path.join(eachPluginPath, eachGizmo[0]).replace('\\', '/') 926 | 927 | if os.path.isfile(gizmoPath): 928 | 929 | # list item: path to gizmo file, size of gizmo file, list of all nodes using the gizmo 930 | readData.append( [gizmoPath, os.path.getsize(gizmoPath), eachGizmo[1]] ) 931 | 932 | nodeI += 1 933 | 934 | if not silent: 935 | nuke.executeInMainThread(TotalProgressUpdate, args=(int(float(iNode) / float(prTotal) * 100) + 50)) 936 | 937 | iNode += 1 938 | 939 | return readData 940 | 941 | 942 | 943 | # function that copies files on all platforms, and creates intermediate directories if needed 944 | # it has some file-specific actions (writing the custom init.py file for copied gizmos, and relinking the nuke scripts) 945 | def ProcessFile(fileFrom, fileTo, writeInit, firstInit, relinkMethod, relinkFonts): 946 | 947 | global WIU_MediaDataNotIgnored 948 | 949 | # caught errors to return 950 | err = '' 951 | 952 | try: 953 | 954 | # create intermediate folders 955 | ToParentFolder = os.path.dirname(fileTo) 956 | if not os.path.isdir(ToParentFolder): 957 | os.makedirs( ToParentFolder ) 958 | 959 | # copy file 960 | shutil.copy2(fileFrom, fileTo) 961 | 962 | 963 | # update init.py/menu.py file 964 | if writeInit: 965 | 966 | # write menu.py/init.py file - file path, is in root path (bool) 967 | def WriteFile(fileName, root): 968 | 969 | # which file 970 | filePath = WIU_PackedPath + '/GIZMOS/' + ('Collected/' if not root else '') + fileName + '.py' 971 | 972 | # try to remove it if logging just started 973 | if firstInit: 974 | try: 975 | os.remove(filePath) 976 | except Exception as e: 977 | pass 978 | 979 | # open file 980 | f = open(filePath, "a+") 981 | 982 | strText = '' 983 | if firstInit: 984 | strText += '# Generated by ' + WIU_Title 985 | 986 | if root: 987 | strText += '\n#\n# Place all contents of the /GIZMOS folder in your /user/.nuke directory to install the necessary gizmos for the collected Nuke script.\n# If an init.py file already exists in /users/.nuke, simply append the following line to that file (instead of overwriting it with this one):\n\n' 988 | strText += "\nnuke.pluginAddPath(\'./Collected\')" 989 | 990 | else: 991 | strText += '\n\n' 992 | 993 | if not root: 994 | 995 | if fileName == 'init': 996 | 997 | gizmoFolderName = fileTo.split('/')[-2] 998 | strText += '\nnuke.pluginAddPath(\"./' + gizmoFolderName + "\")" 999 | 1000 | elif fileName == 'menu': 1001 | 1002 | gizmoFolderName = fileTo.split('/')[-2] 1003 | strText += "\nnuke.toolbar(\"Nodes\").addCommand(\"Collected/" + gizmoFolderName + "\", \"nuke.createNode('" + gizmoFolderName + ".gizmo')\")" 1004 | 1005 | # write and close 1006 | f.write(strText) 1007 | f.close() 1008 | 1009 | 1010 | # write GIZMOS/init.py 1011 | if firstInit: 1012 | WriteFile('init', True) 1013 | 1014 | # GIZMOS/Collected/init.py 1015 | WriteFile('init', False) 1016 | 1017 | #GIZMOS/Collected/menu.py 1018 | WriteFile('menu', False) 1019 | 1020 | 1021 | # relink nuke script 1022 | if relinkMethod is not -1: 1023 | 1024 | if silent: 1025 | print('\n' + WIU_Log + 'Opening temporary Nuke comp in terminal to relink' + (' (relative)' if relinkMethod == 1 else '')) 1026 | 1027 | # quotation mark 1028 | q = '\"' 1029 | 1030 | # temp terminal file 1031 | filePath = WIU_PackedPath + '/WrapItUp_Temp-RELINK_' + str(relinkMethod) + '.py' 1032 | 1033 | # get nk path 1034 | scriptpath = PackedPath(WIU_OtherData[0][0], i= -2 - relinkMethod) 1035 | 1036 | 1037 | # prepare list of things to do (node name, knob name, new value) 1038 | vList = [] 1039 | 1040 | # fonts (if needed) 1041 | if relinkFonts and fonts: 1042 | vList.append([ 'root', 'free_type_font_path', PackedPath(WIU_OtherData[3][0], i=-4, fontFolder=WIU_OtherData[3][0], settingFontPath=True) ]) #font path 1043 | 1044 | # project directory (if needed) 1045 | if WIU_ProjectDir: 1046 | vList.append([ 'root', 'project_directory', PackedPath('', project_dir = True) ]) #new project directory 1047 | 1048 | # for all media 1049 | if media: 1050 | for d in WIU_MediaData: 1051 | # all knobs should be relinked 1052 | n = 0 1053 | for eachNode in d[0]: 1054 | # do not relink files that aren't copied, or when in project directory 1055 | if (silent or (d in WIU_MediaDataNotIgnored)) and not d[6]: 1056 | vList.append([eachNode.fullName(), d[1][n], PackedPath( d[3], nodeName=getNodeNames(d[0], us=True), project_dir = d[6] )]) 1057 | n += 1 1058 | 1059 | 1060 | # write to pyText 1061 | pyText = '# Generated by ' + WIU_Title + '\n#\n# This is a temporary file used to relink nuke scripts. It can be removed.\n\n' 1062 | pyText += 'nuke.scriptOpen(' + q + scriptpath + q + ')\n' 1063 | 1064 | for v in vList: 1065 | 1066 | # replace path for relinked relative 1067 | if relinkMethod == 1: 1068 | v[2] = v[2].replace(os.path.dirname(scriptpath), '[python {nuke.script_directory()}]') 1069 | 1070 | pyText += '\n' 1071 | pyText += 'n = nuke.toNode(' + q + v[0] + q + ')\n' 1072 | pyText += 'n[' + q + v[1] + q + '].setValue(' + q + v[2] + q + ')\n' 1073 | 1074 | pyText += '\nnuke.scriptSave(' + q + scriptpath + q + ')' 1075 | 1076 | 1077 | try: 1078 | 1079 | # make py file 1080 | f = open(filePath, "w+") 1081 | # write 1082 | f.write(pyText) 1083 | # close 1084 | f.close() 1085 | 1086 | license = getLic() 1087 | args = [WIU_AppPath, license, '-q', filePath] 1088 | subprocess.call(args) 1089 | 1090 | # remove the remporary terminal file 1091 | try: 1092 | os.remove(filePath) 1093 | except Exception as e1: 1094 | err += 'Could not remove temporary file for relinking: ' + filePath + '\n' + str(e1) + '\n\n' 1095 | 1096 | # try to remove the autosave file 1097 | try: 1098 | os.remove(scriptpath + '~') 1099 | except Exception as e4: 1100 | pass 1101 | 1102 | except Exception as e2: 1103 | err += 'Could not write/execute temporary file for relinking: ' + filePath + '\n' + str(e2) + '\n\n' 1104 | 1105 | 1106 | if silent: 1107 | print(WIU_Log + 'End of Nuke block' + '\n') 1108 | 1109 | 1110 | return err 1111 | 1112 | 1113 | except Exception as e3: 1114 | return str(e3) 1115 | 1116 | 1117 | 1118 | # read comp 1119 | def Refresh(): 1120 | window.setEnabled(False) 1121 | WTotalProgress.setVisible(True) 1122 | threading.Thread(target=RefreshThreaded, args=()).start() 1123 | 1124 | 1125 | 1126 | # threaded refresh 1127 | def RefreshThreaded(): 1128 | 1129 | global WIU_MediaData 1130 | global WIU_OtherData 1131 | 1132 | # get all paths and information from the current comp 1133 | WIU_MediaData = ReadCompMediaData() 1134 | WIU_OtherData = ReadCompOtherData() 1135 | 1136 | # if silent, update ui 1137 | if not silent: 1138 | nuke.executeInMainThread(RefreshUI, args=()) 1139 | 1140 | # if not, return all data for silent mode 1141 | else: 1142 | refreshRtn = RefreshUI() 1143 | return refreshRtn 1144 | 1145 | 1146 | 1147 | # apply read data to WrapItUp window 1148 | def RefreshUI(): 1149 | 1150 | global WIU_MediaData 1151 | global WIU_OtherData 1152 | 1153 | global WIU_SilentReturn 1154 | global WIU_SilentList 1155 | 1156 | # if silent, return this list 1157 | WIU_SilentReturn = [] 1158 | 1159 | if not silent: 1160 | # clear list 1161 | WListCopyPaths.clear() 1162 | WListIgnorePaths.clear() 1163 | 1164 | # close if comp has not been saved 1165 | if not WIU_OtherData: 1166 | return 1167 | 1168 | # list count 1169 | iCopy = 0 1170 | item = "nk\t\t" + WIU_OtherData[0][0].split('/')[-1] if not silent else "nk\t\t\t\t" + WIU_OtherData[0][0].split('/')[-1] 1171 | if not silent: 1172 | # add current nuke script to copy list 1173 | WListCopyPaths.addItem(item) 1174 | WListCopyPaths.item(iCopy).setData(QtCore.Qt.UserRole, -1) 1175 | WListCopyPaths.item(iCopy).setForeground(QtGui.QColor(150, 150, 150)) 1176 | iCopy += 1 1177 | else: 1178 | WIU_SilentList.append([item, -1]) 1179 | WIU_SilentReturn.append(item) 1180 | 1181 | iCopy += RelinksInList() 1182 | iCopy += AddOnsInList() 1183 | 1184 | # go through each found fileknob value 1185 | iIgnore = 0 1186 | i = 0 1187 | for dataItem in WIU_MediaData: 1188 | 1189 | # check if the path exists 1190 | existsBool = dataItem[2] 1191 | disabled = '* ' if dataItem[7] else '' 1192 | exists = '' if existsBool else 'MISSING ' 1193 | 1194 | # make an item for the list 1195 | nodeName = getNodeNames(dataItem[0]) 1196 | extraTab = '\t' if len(nodeName) < 13 else '' 1197 | item = disabled + exists + nodeName + '\t' + extraTab + dataItem[3] if not silent else exists + nodeName + '\t\t' + extraTab + dataItem[3] 1198 | 1199 | if not silent: 1200 | 1201 | # add to the 'to copy' list 1202 | if(existsBool): 1203 | 1204 | # add item 1205 | WListCopyPaths.addItem(item) 1206 | 1207 | # add data to item 1208 | WListCopyPaths.item(iCopy).setData(QtCore.Qt.UserRole, i) 1209 | 1210 | # count 1211 | iCopy += 1 1212 | 1213 | # add to the 'ignore' list 1214 | else: 1215 | 1216 | # add item 1217 | WListIgnorePaths.addItem(item) 1218 | 1219 | # add data to item 1220 | WListIgnorePaths.item(iIgnore).setData(QtCore.Qt.UserRole, i) 1221 | 1222 | # grayed out colours 1223 | WListIgnorePaths.item(iIgnore).setForeground(QtGui.QColor(150, 150, 150)) 1224 | 1225 | # count 1226 | iIgnore += 1 1227 | 1228 | 1229 | else: 1230 | 1231 | if existsBool: 1232 | 1233 | WIU_SilentList.append([item, i]) 1234 | WIU_SilentReturn.append(item) 1235 | 1236 | 1237 | # overall count 1238 | i += 1 1239 | 1240 | if not silent: 1241 | 1242 | UpdateTotalSize() 1243 | 1244 | WTotalProgress.setVisible(False) 1245 | window.setEnabled(True) 1246 | 1247 | else: 1248 | 1249 | UpdateTotalSize() 1250 | 1251 | if startnow: 1252 | StartCopyRtn = StartCopy() 1253 | return StartCopyRtn 1254 | else: 1255 | return WIU_SilentReturn 1256 | 1257 | 1258 | 1259 | # move item from copy to ignore 1260 | def SendToIgnore(): 1261 | 1262 | for eachSelectedItem in WListCopyPaths.selectedItems(): 1263 | 1264 | # only copy over if allowed 1265 | selI = eachSelectedItem.data(QtCore.Qt.UserRole) 1266 | if selI >= 0: 1267 | WListCopyPaths.takeItem(WListCopyPaths.row(eachSelectedItem)) 1268 | WListIgnorePaths.addItem(eachSelectedItem) 1269 | 1270 | UpdateTotalSize() 1271 | 1272 | 1273 | 1274 | # move item from ignore to copy 1275 | def SendToCopy(): 1276 | 1277 | for eachSelectedItem in WListIgnorePaths.selectedItems(): 1278 | 1279 | # get data 1280 | selI = eachSelectedItem.data(QtCore.Qt.UserRole) 1281 | selData = WIU_MediaData[selI] 1282 | selExists = selData[2] 1283 | 1284 | # only copy over if it exists 1285 | if selExists: 1286 | WListIgnorePaths.takeItem(WListIgnorePaths.row(eachSelectedItem)) 1287 | WListCopyPaths.addItem(eachSelectedItem) 1288 | 1289 | UpdateTotalSize() 1290 | 1291 | 1292 | 1293 | # update total file size count 1294 | def UpdateTotalSize(): 1295 | 1296 | global WIU_TotalSize 1297 | 1298 | # add up all data from each item in the copy list 1299 | WIU_TotalSize = 0 1300 | 1301 | itemLen = WListCopyPaths.count() if not silent else len(WIU_SilentList) 1302 | for i in range(itemLen): 1303 | 1304 | itemData = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) if not silent else WIU_SilentList[i][1] 1305 | 1306 | Media = False 1307 | if itemData >= 0: 1308 | Media = True 1309 | 1310 | if Media: 1311 | for eachFile in WIU_MediaData[itemData][4]: 1312 | WIU_TotalSize += eachFile[1] 1313 | else: 1314 | WIU_TotalSize += WIU_OtherData[abs(itemData)-1][1] 1315 | 1316 | 1317 | # set label 1318 | if not silent: 1319 | WTotalCopySize.setText( BytesToString(WIU_TotalSize) ) 1320 | 1321 | 1322 | 1323 | # selection hanged on copy list 1324 | def CopyListSelectionChanged(): 1325 | 1326 | WListIgnorePaths.clearSelection() 1327 | UpdateLabels(True) 1328 | 1329 | 1330 | 1331 | # selection hanged on ignore list 1332 | def IgnoreListSelectionChanged(): 1333 | 1334 | WListCopyPaths.clearSelection() 1335 | UpdateLabels(False) 1336 | 1337 | 1338 | 1339 | # makes long path -> ../long path if it is too long 1340 | def shortenPath(strPath, iChars): 1341 | 1342 | newPath = '...' + strPath[-iChars:] 1343 | short = False 1344 | if len(strPath) > iChars: 1345 | short = True 1346 | 1347 | return newPath if short else strPath 1348 | 1349 | 1350 | 1351 | # convert a list of nodes to a string of node names - list of nodes, underscore, item index (for data check) 1352 | def getNodeNames(nodeList, us = False, i = 0): 1353 | 1354 | strNodes = '' 1355 | 1356 | maxNodeCount = 5 1357 | nodeCount = 0 1358 | 1359 | for eachNode in nodeList: 1360 | if(nodeCount >= maxNodeCount and i == 0): # check if there are too many node names 1361 | strNodes += 'etc' # 'etc' to indicate there were more nodes but the name got too long 1362 | return strNodes # return right away 1363 | 1364 | nodeCount += 1 1365 | 1366 | strNodes += (eachNode.fullName() if (i <= -5 or i >= 0) else eachNode) + ('_' if us else ' ') 1367 | 1368 | strNodes = strNodes[:-1] 1369 | 1370 | return strNodes 1371 | 1372 | 1373 | 1374 | # convert a list of knobs to a string of node names - list of knobs, underscore 1375 | def getKnobNames(knobList, us = False): 1376 | 1377 | strKnobs = '' 1378 | 1379 | for eachKnob in knobList: 1380 | strKnobs += eachKnob + ('_' if us else ' ') 1381 | 1382 | strKnobs = strKnobs[:-1] 1383 | 1384 | return strKnobs 1385 | 1386 | 1387 | 1388 | # update the labels underneath the lists 1389 | def UpdateLabels(CopyList): 1390 | 1391 | # disable button 1392 | WGoToNode.setEnabled(False) 1393 | 1394 | # get all selected objects 1395 | sel = [] 1396 | if(CopyList): 1397 | sel = WListCopyPaths.selectedItems() 1398 | else: 1399 | sel = WListIgnorePaths.selectedItems() 1400 | 1401 | if len(sel) == 1: 1402 | 1403 | # get last selected object 1404 | selVal = sel[0] 1405 | 1406 | # get data index 1407 | selI = selVal.data(QtCore.Qt.UserRole) 1408 | 1409 | # set data 1410 | selData = [] 1411 | selOtherData = [] 1412 | 1413 | Media = False 1414 | if selI >= 0: 1415 | Media = True 1416 | 1417 | curritempath = '' 1418 | packeditempath = '' 1419 | curritemfiles = '' 1420 | curritemsize = 0 1421 | 1422 | # get media data 1423 | if(Media): 1424 | selData = WIU_MediaData[selI] 1425 | curritempath = selData[3] 1426 | packeditempath = PackedPath( curritempath, nodeName=getNodeNames(selData[0], us=True), project_dir = selData[6] ) 1427 | curritemfiles = str( len(selData[4]) ) 1428 | curritemsize = selData[5] 1429 | 1430 | WGoToNode.setToolTip( getNodeNames(selData[0], us=False) ) 1431 | WGoToNode.setEnabled(True) 1432 | 1433 | # project directory prefix 1434 | if(selData[6]): 1435 | curritempath = ProjectDirectory(curritempath) 1436 | 1437 | 1438 | # get other data 1439 | else: 1440 | 1441 | # convert selI to positive integers, matching to the data array 1442 | selIConverted = abs(selI) - 1 1443 | 1444 | # get data 1445 | selData = WIU_OtherData[selIConverted] 1446 | 1447 | curritempath = selData[0] 1448 | packeditempath = PackedPath(curritempath, i = selI) 1449 | curritemfiles = '1' 1450 | 1451 | # set amount of files for fonts 1452 | if selI == -4: 1453 | curritemfiles = str(len(selData[2])) 1454 | packeditempath = WIU_PackedPath + '/' + curritempath[:-1].split('/')[-1] 1455 | 1456 | curritemsize = selData[1] 1457 | 1458 | 1459 | # TCL eval 1460 | curritempath = evalTCL(curritempath) 1461 | 1462 | 1463 | # set label texts and tooltips 1464 | labelLength = 100 1465 | 1466 | # get shortened path 1467 | curritempathShortened = shortenPath(curritempath, labelLength) 1468 | # set as text 1469 | WCurrItemPath.setText(curritempathShortened) 1470 | # set full path as tooltip 1471 | WCurrItemPath.setToolTip(curritempath) 1472 | 1473 | # get shortened path 1474 | packeditempathShortened = shortenPath(packeditempath, labelLength) 1475 | # set as text 1476 | WPackedItemPath.setText(packeditempathShortened) 1477 | # set full path as tooltip 1478 | WPackedItemPath.setToolTip(packeditempath) 1479 | 1480 | 1481 | WCurrItemFiles.setText(curritemfiles) 1482 | WCurrItemSize.setText( BytesToString(curritemsize) ) 1483 | 1484 | WIgnoredLabel.setVisible(not CopyList) 1485 | 1486 | WGoToFolder.setEnabled(True) 1487 | 1488 | else: 1489 | 1490 | selectItemStr = '-' 1491 | 1492 | WCurrItemPath.setText(selectItemStr) 1493 | WPackedItemPath.setText(selectItemStr) 1494 | WCurrItemFiles.setText(selectItemStr) 1495 | WCurrItemSize.setText(selectItemStr) 1496 | 1497 | WIgnoredLabel.setVisible(False) 1498 | 1499 | WGoToFolder.setEnabled(False) 1500 | 1501 | 1502 | 1503 | # update the labels underneath the lists 1504 | def UpdateItemInfo(n = False, pd = False): 1505 | 1506 | global WIU_ParentDirCount 1507 | global WIU_NodeNameFolder 1508 | 1509 | if pd: 1510 | WIU_ParentDirCount = WParentDirectories.value() 1511 | if n: 1512 | WIU_NodeNameFolder = WNodeNameFolder.isChecked() 1513 | 1514 | # check which list the currently selected item is in, and update its info 1515 | if WIgnoredLabel.isVisible(): 1516 | IgnoreListSelectionChanged() 1517 | else: 1518 | CopyListSelectionChanged() 1519 | 1520 | 1521 | 1522 | # on packed path changed 1523 | def PackedPathChanged(): 1524 | 1525 | # allow editing of global 1526 | global WIU_PackedPath 1527 | 1528 | # update WIU_PackedPath 1529 | WIU_PackedPath = WPackedPath.text() 1530 | 1531 | UpdateItemInfo() 1532 | 1533 | 1534 | 1535 | # arguments are switches for relink and relink, relative 1536 | def RelinksInList(r = False, rr = False): 1537 | 1538 | global WIU_Relink 1539 | global WIU_RelinkRelative 1540 | 1541 | if not silent: 1542 | 1543 | if r: 1544 | WIU_Relink = not WIU_Relink 1545 | if rr: 1546 | WIU_RelinkRelative = not WIU_RelinkRelative 1547 | 1548 | 1549 | # get each item 1550 | i = 0 1551 | while i < WListCopyPaths.count(): 1552 | 1553 | # get data 1554 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1555 | 1556 | # delete if relink 1557 | if d == -2 or d == -3: 1558 | WListCopyPaths.takeItem(i) 1559 | i = 0 1560 | else: 1561 | i += 1 1562 | 1563 | # if no UI 1564 | else: 1565 | WIU_Relink = relinked 1566 | WIU_RelinkRelative = relativerelinked 1567 | 1568 | 1569 | iCopyCount = 0 1570 | 1571 | 1572 | # add current nuke script to copy list, this time relative relinked 1573 | if WIU_RelinkRelative: 1574 | item = "nk (relative relinked)\t" + WIU_OtherData[0][0].split('/')[-1] 1575 | 1576 | if not silent: 1577 | index = 1 1578 | WListCopyPaths.insertItem(index, item) 1579 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -3) 1580 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1581 | iCopyCount += 1 1582 | 1583 | else: 1584 | WIU_SilentList.append([item, -3]) 1585 | WIU_SilentReturn.append(item) 1586 | 1587 | 1588 | # add current nuke script to copy list, this time relinked 1589 | if WIU_Relink: 1590 | item = "nk (relinked)\t\t" + WIU_OtherData[0][0].split('/')[-1] 1591 | 1592 | if not silent: 1593 | index = 1 1594 | WListCopyPaths.insertItem(index, item) 1595 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -2) 1596 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1597 | iCopyCount += 1 1598 | 1599 | else: 1600 | WIU_SilentList.append([item, -2]) 1601 | WIU_SilentReturn.append(item) 1602 | 1603 | 1604 | # for refresh function item count 1605 | return iCopyCount 1606 | 1607 | 1608 | 1609 | # show/hide addon items in list, arguments for toggles 1610 | def AddOnsInList(f = False, g = False): 1611 | 1612 | global WIU_Gizmo 1613 | global WIU_Fonts 1614 | 1615 | 1616 | if not silent: 1617 | 1618 | if g: 1619 | WIU_Gizmo = not WIU_Gizmo 1620 | if f: 1621 | WIU_Fonts = not WIU_Fonts 1622 | 1623 | # remove all addon items 1624 | i = 0 1625 | while i < WListCopyPaths.count(): 1626 | 1627 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1628 | 1629 | if d <= -4: 1630 | WListCopyPaths.takeItem(i) 1631 | i = 0 1632 | else: 1633 | i += 1 1634 | 1635 | # if no UI is loaded 1636 | else: 1637 | WIU_Gizmo = gizmos 1638 | WIU_Fonts = fonts 1639 | 1640 | 1641 | iCopyCount = 0 1642 | 1643 | # determine index of items 1644 | index = 1 1645 | if not silent: 1646 | if WRelativeRelink.isChecked(): 1647 | index += 1 1648 | 1649 | if WRelinkPaths.isChecked(): 1650 | index += 1 1651 | 1652 | if WIU_Gizmo: 1653 | 1654 | # iterate through all items in WIU_OtherData, except for the first 4 (0 - 3, which are nuke script, - relinked, - relinked relative, font) 1655 | for eachGizmo in range(len(WIU_OtherData) - 4): 1656 | 1657 | item = "gizmo\t\t" + WIU_OtherData[4 + eachGizmo][0] if not silent else "gizmo\t\t\t" + WIU_OtherData[4 + eachGizmo][0] 1658 | 1659 | if not silent: 1660 | 1661 | WListCopyPaths.insertItem(index, item) 1662 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -5 - eachGizmo) 1663 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1664 | iCopyCount += 1 1665 | 1666 | else: 1667 | 1668 | WIU_SilentList.append([item, -5 - eachGizmo]) 1669 | WIU_SilentReturn.append(item) 1670 | 1671 | FontPathExists = os.path.isdir(WIU_OtherData[3][0]) 1672 | if WIU_Fonts and FontPathExists: 1673 | 1674 | item = "font folder\t\t" + WIU_OtherData[3][0] if not silent else "font folder\t\t\t" + WIU_OtherData[3][0] 1675 | 1676 | if not silent: 1677 | 1678 | WListCopyPaths.insertItem(index, item) 1679 | WListCopyPaths.item(index).setData(QtCore.Qt.UserRole, -4) 1680 | WListCopyPaths.item(index).setForeground(QtGui.QColor(150, 150, 150)) 1681 | iCopyCount += 1 1682 | 1683 | else: 1684 | 1685 | WIU_SilentList.append([item, -4]) 1686 | WIU_SilentReturn.append(item) 1687 | 1688 | 1689 | # for refresh function item count 1690 | return iCopyCount 1691 | 1692 | 1693 | 1694 | def ChoosePackedPath(): 1695 | 1696 | # open nuke file dialog 1697 | chosenPath = nuke.getFilename('WrapItUp - choose the folder to copy the script and all media to') 1698 | 1699 | if chosenPath is not None: 1700 | 1701 | # make sure it is an existing folder 1702 | if os.path.isfile(chosenPath): 1703 | chosenPath = os.path.dirname(chosenPath) 1704 | 1705 | # cut off the last / if it is there 1706 | if chosenPath.endswith('/'): 1707 | chosenPath = chosenPath[:-1] 1708 | 1709 | # set value to text knob 1710 | if os.path.isdir(chosenPath): 1711 | # set packed path 1712 | WPackedPath.setText(chosenPath) 1713 | 1714 | # path does not exist, ask to create the folders instead 1715 | else: 1716 | q = nuke.ask('Path does not exist:\n\n' + chosenPath + '\n\nDo you want to choose this path anyway? Any non-existing folders will be created when files are copied.') 1717 | if q: 1718 | if chosenPath.endswith('/'): 1719 | chosenPath = chosenPath[:-1] 1720 | WPackedPath.setText(chosenPath) 1721 | 1722 | 1723 | # regain focus on form 1724 | window.activateWindow() 1725 | 1726 | 1727 | 1728 | # function to control buttons on form 1729 | def ButtonsAllowed(YesOrNo): 1730 | 1731 | # disable all buttons that could interfere (except for the interrupt button of course) 1732 | WSendToCopy.setEnabled(YesOrNo) 1733 | WSendToIgnore.setEnabled(YesOrNo) 1734 | WStart.setEnabled(YesOrNo) 1735 | WRefresh.setEnabled(YesOrNo) 1736 | WParentDirectories.setEnabled(YesOrNo) 1737 | WRelinkPaths.setEnabled(YesOrNo) 1738 | WRelativeRelink.setEnabled(YesOrNo) 1739 | WNodeNameFolder.setEnabled(YesOrNo) 1740 | WPackedPath.setEnabled(YesOrNo) 1741 | WChoosePackedPathButton.setEnabled(YesOrNo) 1742 | WLParentDirectories.setEnabled(YesOrNo) 1743 | WLPath.setEnabled(YesOrNo) 1744 | WContinueOnError.setEnabled(YesOrNo) 1745 | WCSVSeparator.setEnabled(YesOrNo) 1746 | WLCSVSeparator.setEnabled(YesOrNo) 1747 | WLicense.setEnabled(YesOrNo) 1748 | WLLicense.setEnabled(YesOrNo) 1749 | WExitOnFinish.setEnabled(YesOrNo) 1750 | WCopyFontDir.setEnabled(YesOrNo) 1751 | WCopyGizmos.setEnabled(YesOrNo) 1752 | 1753 | # make progress bar visible 1754 | WTotalProgress.setVisible(not YesOrNo) 1755 | WItemProgress.setVisible(not YesOrNo) 1756 | WCurrentCopyItem.setVisible(not YesOrNo) 1757 | 1758 | 1759 | 1760 | # prepare list to copy 1761 | def PrepareCopy(): 1762 | 1763 | global WIU_TotalSize 1764 | global WIU_SilentList 1765 | global WIU_MediaDataNotIgnored 1766 | 1767 | # bool for ProcessFiles 1768 | relinkFonts = False 1769 | 1770 | # get all items in data list 1771 | itemsToCopy = [] 1772 | listLen = WListCopyPaths.count() if not silent else len(WIU_SilentList) 1773 | for i in range(listLen): 1774 | 1775 | # get index 1776 | dataItem = 0 1777 | if not silent: 1778 | dataItem = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 1779 | else: 1780 | dataItem = WIU_SilentList[i][1] 1781 | 1782 | # assign custom data for nuke script to item 1783 | # make list with item index and data combined, so the index does not get lost 1784 | if(dataItem < 0): 1785 | itemsToCopy.append( [dataItem, WIU_OtherData[abs(dataItem) - 1]] ) 1786 | # all other media files 1787 | else: 1788 | itemsToCopy.append( [dataItem, WIU_MediaData[dataItem]] ) 1789 | 1790 | # store this index for relinking only relevant media 1791 | WIU_MediaDataNotIgnored.append(WIU_MediaData[dataItem]) 1792 | 1793 | 1794 | 1795 | 1796 | # get all files involved 1797 | fileList = [] 1798 | i = 0 1799 | addedSizes = 0.0 1800 | 1801 | for j in itemsToCopy: 1802 | 1803 | # index, data 1804 | k = j[0] 1805 | l = j[1] 1806 | 1807 | # media 1808 | if k >= 0 and media: 1809 | 1810 | # item size 1811 | itemSizeCounting = 0 1812 | 1813 | # get list of each file, file size 1814 | for eachFileS in l[4]: 1815 | 1816 | # get path of destination file path 1817 | eachFileTo = PackedPath(eachFileS[0], nodeName = getNodeNames(l[0], us = True), project_dir = l[6]) 1818 | 1819 | # count sizes together 1820 | itemSizeCounting += eachFileS[1] 1821 | 1822 | # single files of 0 bytes should still be copied (even though it does not make sense to have them in your script), make them 1 byte for progress bar 1823 | currSize = l[5] if int(l[5]) > 0 else 1 1824 | # get ratio for progress bar % 1825 | sequenceSizeRatio = int (float(itemSizeCounting) / float(currSize) * 100) 1826 | 1827 | # set file, file path to copy to, list item index, file size in bytes, size ratio within sequence for progress bar, nodes, knobs, original index 1828 | fileList.append([ eachFileS[0], eachFileTo, i, eachFileS[1], sequenceSizeRatio, l[0], l[1], k ]) 1829 | 1830 | 1831 | # nuke script 1832 | if k == -1: 1833 | 1834 | filePath = l[0] 1835 | fileSize = l[1] 1836 | fileTo = PackedPath(filePath, i = k) 1837 | 1838 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1839 | 1840 | # nuke script relinked 1841 | if k == -2: 1842 | 1843 | filePath = l[0] 1844 | fileSize = l[1] 1845 | fileTo = PackedPath(filePath, i = k) 1846 | 1847 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1848 | 1849 | # nuke script relinked relative 1850 | if k == -3: 1851 | 1852 | filePath = l[0] 1853 | fileSize = l[1] 1854 | fileTo = PackedPath(filePath, i = k) 1855 | 1856 | fileList.append([ filePath, fileTo, i, fileSize, 100, ['root'], ['name'], k ]) 1857 | 1858 | # font directory 1859 | if k == -4: 1860 | 1861 | # set bool 1862 | relinkFonts = True 1863 | 1864 | # item size 1865 | itemSizeCounting = 0 1866 | 1867 | # get each font, size combination 1868 | for eachFileS in l[2]: 1869 | 1870 | # get path of destination file path 1871 | if l[0].endswith('/'): 1872 | l[0] = l[0][:-1] 1873 | eachFileTo = PackedPath(eachFileS[0], i = k, fontFolder = l[0]) 1874 | 1875 | # count sizes together 1876 | itemSizeCounting += eachFileS[1] 1877 | 1878 | # progress bar value (l[1] if not 0, else 1) 1879 | maxVal = float(l[1] * 100) 1880 | maxVal = 1 if maxVal == 0 else maxVal 1881 | sequenceSizeRatio = int ( float(itemSizeCounting) / (maxVal) ) 1882 | 1883 | # set file, file path to copy to, list item index, file size in bytes, size ratio within sequence for progress bar, nodes, knobs, original index 1884 | fileList.append( [eachFileS[0], eachFileTo, i, eachFileS[1], sequenceSizeRatio, ['root'], ['free_type_font_path'], k] ) 1885 | 1886 | # gizmo 1887 | if k <= -5: 1888 | 1889 | filePath = l[0] 1890 | fileSize = l[1] 1891 | nodes = l[2] 1892 | fileTo = PackedPath(filePath, i = k) 1893 | 1894 | fileList.append([ filePath, fileTo, i, fileSize, 100, nodes, ['-'], k ]) 1895 | 1896 | 1897 | # count 1898 | i += 1 1899 | 1900 | 1901 | return [fileList, relinkFonts] 1902 | 1903 | 1904 | 1905 | # function to append text to log file 1906 | def WriteLog(logText, first = False): 1907 | 1908 | # which file 1909 | filePath = WIU_PackedPath + '/log.csv' 1910 | 1911 | # try to remove it if logging just started 1912 | if first: 1913 | 1914 | try: 1915 | os.remove(filePath) 1916 | except Exception as e: 1917 | pass 1918 | 1919 | 1920 | try: 1921 | 1922 | # open file 1923 | f = open(filePath, "a+") 1924 | 1925 | # write and close 1926 | f.write(logText + '\n') 1927 | f.close() 1928 | 1929 | except Exception as e: 1930 | pass 1931 | 1932 | 1933 | 1934 | # convert data to CSV format 1935 | def CSV(values): 1936 | 1937 | CSVtext = '' 1938 | csv = WCSVSeparator.currentText() if not silent else (',' if csvcommas else ';') 1939 | for d in values: 1940 | CSVtext += '\"' + str(d) + '\"' + (csv) 1941 | 1942 | return CSVtext 1943 | 1944 | 1945 | 1946 | def getLic(): 1947 | 1948 | license = WLicense.currentText() if not silent else ('-ti' if licinteractive else '-t') 1949 | return license 1950 | 1951 | 1952 | 1953 | # threaded function for copying/relinking - list of files to process with all necessary data, fonts should be relinked (bool) 1954 | def ThreadedCopy(fileList, relinkFonts): 1955 | 1956 | # error count 1957 | totalErrorCount = 0 1958 | 1959 | # count size 1960 | totalSizeCount = 0 1961 | totalSizeStored = WIU_TotalSize 1962 | 1963 | listCount = (WListCopyPaths.count() if not silent else len(WIU_SilentList)) - 1 1964 | 1965 | # write first lines of log 1966 | nukever = str(nuke.NUKE_VERSION_STRING) 1967 | nukescript = nuke.root().name() if not silent else nk 1968 | params = CSV(["Used command-line/python function", str(silent)]) + '\n' + CSV(["Script path", nukescript]) + '\n' + CSV(["Folders with node names", str(WIU_NodeNameFolder)]) + '\n' + CSV(["Parent directory count", str(WIU_ParentDirCount)]) + '\n' + CSV(["Relinked", str(WIU_Relink)]) + '\n' + CSV(["Relative relinked", str(WIU_RelinkRelative)]) + '\n' + CSV(["Fonts", str(WIU_Fonts)]) + '\n' + CSV(["Gizmos", str(WIU_Gizmo)]) + '\n' + CSV(["License", 'nuke_i' if getLic() == '-ti' else 'nuke_r']) 1969 | WriteLog( WIU_Title + '\n' + 'Nuke version: ' + nukever + '\n\n' + params + '\n\n\n' + CSV(['TIME', 'STATUS', 'FILE (FROM)', 'FILE (TO)', 'NODE', 'KNOB', 'SIZE', 'RETURN']) + '\n', first = True ) 1970 | 1971 | # copy files 1972 | prevI = -1 1973 | firstInit = True 1974 | i = 0 1975 | iM = len(fileList) 1976 | c = False 1977 | lastItemFailed = False 1978 | for f in fileList: 1979 | 1980 | # get current time 1981 | timestr = time.strftime("%c") 1982 | 1983 | # interrupt check 1984 | if WIU_Interrupted: 1985 | WriteLog( CSV([timestr, 'PROCESS WAS CANCELLED BY USER']) ) 1986 | nuke.executeInMainThread(Finished, args=('USER')) 1987 | sys.exit() 1988 | 1989 | 1990 | relinkMethod = 0 if f[7] == -2 else (1 if f[7] == -3 else -1) # 0 if relink, 1 if relink relative, -1 if none 1991 | suffix = ' copy/relink' if relinkMethod is not -1 else '' # suffix for relinked nk scripts 1992 | labelText = shortenPath(f[0] + suffix, 20) # get shorter path for current copying file, add suffix 1993 | 1994 | if not silent: 1995 | 1996 | # set label text 1997 | nuke.executeInMainThread(SetCurrCopyItemLabel, args=(labelText, f[0])) 1998 | 1999 | # set working colour 2000 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], True)) 2001 | 2002 | else: 2003 | 2004 | # append size to label text for console 2005 | labelText += ' (' + BytesToString(f[3]) + ')' 2006 | 2007 | 2008 | # start process 2009 | writeInit = f[7] <= -5 # True if in gizmo range of list 2010 | result = ProcessFile(f[0], f[1], writeInit, firstInit, relinkMethod, relinkFonts) 2011 | if writeInit: 2012 | firstInit = False 2013 | 2014 | # add to size count 2015 | totalSizeCount += f[3] 2016 | 2017 | # write log 2018 | if result == '': 2019 | 2020 | WriteLog( CSV([timestr, 'SUCCES', f[0], f[1], getNodeNames(f[5], i=f[7]), getKnobNames(f[6]), BytesToString(f[3])]) ) 2021 | 2022 | # make green 2023 | if(f[2] > prevI): 2024 | if not silent: 2025 | if not lastItemFailed: 2026 | nuke.executeInMainThread(ChangeItemColour, args = (prevI, False, True)) 2027 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], False, True)) 2028 | lastItemFailed = False 2029 | prevI = f[2] 2030 | 2031 | # on error 2032 | else: 2033 | 2034 | WriteLog( CSV([timestr, 'FAILED', f[0], f[1], getNodeNames(f[5], i=f[7]), getKnobNames(f[6]), BytesToString(f[3]), result]) ) 2035 | 2036 | if not silent: 2037 | if not WContinueOnError.isChecked(): 2038 | nuke.executeInMainThread(Finished, args = ([f[0] + '\n\n' + result])) 2039 | 2040 | # make red 2041 | if(f[2] > prevI): 2042 | nuke.executeInMainThread(ChangeItemColour, args = (f[2], False, False)) 2043 | lastItemFailed = True 2044 | prevI = f[2] 2045 | 2046 | totalErrorCount += 1 2047 | 2048 | # set total progress bar 2049 | sizeRatio = float( totalSizeCount ) / float( totalSizeStored ) 2050 | progressVal = int( sizeRatio * 100 ) 2051 | 2052 | 2053 | # set item progress bar 2054 | if not silent: 2055 | nuke.executeInMainThread(ItemProgressUpdate, args = (f[4])) 2056 | nuke.executeInMainThread(TotalProgressUpdate, args = (progressVal)) 2057 | 2058 | # print progress 2059 | else: 2060 | if not (f[7] <= -1 and f[7] >= -3): 2061 | if not c: 2062 | c = True 2063 | print('') 2064 | 2065 | if i == iM - 1: 2066 | labelText = ' ' 2067 | sys.stdout.write(WIU_Log + "Copying files: %d%% \t %s \r" % (progressVal, labelText) ) 2068 | sys.stdout.flush() 2069 | 2070 | i += 1 2071 | 2072 | WriteLog( CSV([timestr, 'FINISHED!']) ) 2073 | 2074 | if not silent: 2075 | nuke.executeInMainThread(Finished, args = ('' if totalErrorCount == 0 else '.')) 2076 | else: 2077 | print('\n\n\n' + WIU_Log + (('Finished! No errors. Out folder path:\n' + WIU_Log + out) if totalErrorCount == 0 else 'Finished copying! There were some errors. Check the log in the out folder:\n' + WIU_Log + out)) 2078 | 2079 | 2080 | 2081 | # set current copy item texts 2082 | def SetCurrCopyItemLabel(strText, strToolTip): 2083 | 2084 | WCurrentCopyItem.setText(strText) 2085 | WCurrentCopyItem.setToolTip(strToolTip) 2086 | 2087 | 2088 | 2089 | # total progress bar update on main thread 2090 | def TotalProgressUpdate(NewValue): 2091 | 2092 | WTotalProgress.setValue(NewValue) 2093 | 2094 | 2095 | 2096 | # item progress bar update on main thread 2097 | def ItemProgressUpdate(NewValue): 2098 | 2099 | WItemProgress.setValue(NewValue) 2100 | 2101 | 2102 | 2103 | # progress bar update on main thread 2104 | def ChangeItemColour(i, loading, Succeeded=False): 2105 | 2106 | C = [] 2107 | 2108 | if Succeeded: 2109 | C = [0, 255, 0] 2110 | else: 2111 | if loading: 2112 | C = [255, 215, 0] 2113 | else: 2114 | C = [255, 0, 0] 2115 | 2116 | 2117 | # 'disabled' colour 2118 | d = WListCopyPaths.item(i).data(QtCore.Qt.UserRole) 2119 | if d < 0 and not loading: 2120 | C = [round(C[0]*.5), round(C[1]*.5), round(C[2]*.5)] 2121 | 2122 | WListCopyPaths.item(i).setForeground(QtGui.QColor(C[0], C[1], C[2])) 2123 | 2124 | 2125 | 2126 | # function to call on finish 2127 | def Finished(isError): 2128 | 2129 | global WIU_Interrupted 2130 | global WIU_Copying 2131 | 2132 | WIU_Interrupted = False 2133 | WIU_Copying = False 2134 | 2135 | 2136 | # exit window when not interrupted by user and exit on finish is enabled 2137 | if ((isError is not 'USER') and (WExitOnFinish.isChecked())): 2138 | 2139 | # do not ask to save script 2140 | try: 2141 | nuke.toNode('root').setModified(False) 2142 | except Exception as e: 2143 | pass 2144 | # exit 2145 | nuke.scriptExit() 2146 | 2147 | 2148 | else: 2149 | 2150 | if(isError == ''): 2151 | 2152 | window.close() 2153 | nuke.message("Finished copying! There were no errors.\nThe folder will open now.") 2154 | OpenFolder(WIU_PackedPath) 2155 | 2156 | 2157 | elif(isError == '.'): 2158 | 2159 | window.close() 2160 | nuke.message("Finished copying! There were some errors.\nThe folder will open now, see the log file for details!") 2161 | OpenFolder(WIU_PackedPath) 2162 | 2163 | elif(isError == 'USER'): 2164 | 2165 | nuke.message("Copying cancelled! The folder will open now.") 2166 | window.activateWindow() 2167 | OpenFolder(WIU_PackedPath) 2168 | 2169 | WInterrupt.setEnabled(True) 2170 | ButtonsAllowed(True) 2171 | Refresh() 2172 | 2173 | else: 2174 | 2175 | nuke.message("ERROR: " + isError + '\n\nSee ./log.txt for more information.') 2176 | WInterrupt.setEnabled(True) 2177 | ButtonsAllowed(True) 2178 | window.activateWindow() 2179 | OpenFolder(WIU_PackedPath) 2180 | Refresh() 2181 | 2182 | 2183 | 2184 | # open folder 2185 | def OpenFolder(folderPath = ''): 2186 | 2187 | if folderPath == '': 2188 | folderPath = WCurrItemPath.toolTip() 2189 | oFolderPath = folderPath 2190 | 2191 | try: 2192 | # get parent dir 2193 | if not os.path.isdir(folderPath): 2194 | folderPath = os.path.dirname(folderPath) 2195 | 2196 | # get project directory folder 2197 | if not os.path.isdir(folderPath): 2198 | folderPath = ProjectDirectory(oFolderPath) 2199 | 2200 | 2201 | if os.path.isdir(folderPath): 2202 | 2203 | if sys.platform == 'win32': 2204 | folderPath = folderPath.replace('/', '\\') 2205 | os.startfile(folderPath) 2206 | else: 2207 | opener ="open" if sys.platform == "darwin" else "xdg-open" 2208 | subprocess.call([opener, folderPath]) 2209 | 2210 | 2211 | else: 2212 | print(folderPath) 2213 | 2214 | except Exception as e: 2215 | pass 2216 | 2217 | 2218 | 2219 | def ToNode(): 2220 | 2221 | # select the nodes 2222 | for i in nuke.allNodes(): 2223 | try: 2224 | i.setSelected(False) 2225 | except Exception as e: 2226 | pass 2227 | 2228 | 2229 | for eachNode in WGoToNode.toolTip().split(' '): 2230 | n = nuke.toNode(eachNode) 2231 | try: 2232 | n.setSelected(True) 2233 | except Exception as e: 2234 | pass 2235 | 2236 | # zoom 2237 | nuke.zoomToFitSelected() 2238 | 2239 | 2240 | 2241 | # start copy 2242 | def StartCopy(): 2243 | 2244 | global WIU_Copying 2245 | global WIU_PackedPath 2246 | 2247 | # check 2248 | q = True 2249 | if not silent: 2250 | q = nuke.ask("Start copying " + WTotalCopySize.text() + '?\n' + 'Destination: ' + WIU_PackedPath + '\nAny existing files will be overwritten.') 2251 | window.activateWindow() 2252 | 2253 | # set out folder 2254 | else: 2255 | WIU_PackedPath = out 2256 | 2257 | if q: 2258 | 2259 | # disable ui 2260 | if not silent: 2261 | ButtonsAllowed(False) 2262 | 2263 | # get list to copy 2264 | preparedCopy = PrepareCopy() 2265 | fileList = preparedCopy[0] 2266 | relinkFonts = preparedCopy[1] 2267 | 2268 | if not silent: 2269 | # run process on different thread 2270 | threading.Thread(target=ThreadedCopy, args=([fileList, relinkFonts])).start() 2271 | else: 2272 | # run threaded copy on main thread in terminal 2273 | ThreadedCopy(fileList, relinkFonts) 2274 | 2275 | WIU_Copying = True 2276 | 2277 | if silent: 2278 | return WIU_SilentReturn 2279 | 2280 | 2281 | 2282 | # interrupt 2283 | def StopCopy(): 2284 | 2285 | global WIU_Interrupted 2286 | global WIU_Copying 2287 | 2288 | # check 2289 | if WIU_Copying: 2290 | 2291 | question = "Are you sure you want to cancel?" 2292 | q = nuke.ask(question) 2293 | 2294 | window.activateWindow() 2295 | 2296 | if q: 2297 | 2298 | WCurrentCopyItem.setText("Cancelling...") 2299 | WCurrentCopyItem.setToolTip('Waiting for the current file copy to complete before stopping the copy process.') 2300 | WIU_Interrupted = True 2301 | WInterrupt.setEnabled(False) 2302 | 2303 | else: 2304 | 2305 | window.close() 2306 | 2307 | 2308 | 2309 | # open root folder 2310 | def GoToRootFolder(): 2311 | if not WIU_PackedPath == '': 2312 | OpenFolder(WIU_PackedPath) 2313 | 2314 | 2315 | 2316 | # catch window closing if the rejected button was not pressed 2317 | def exitForm(): 2318 | if WIU_Copying: 2319 | window.show() 2320 | 2321 | 2322 | 2323 | # show ui if not silent mode 2324 | if not silent: 2325 | 2326 | window = QtWidgets.QDialog() 2327 | ui = Ui_Dialog() 2328 | ui.setupUi(window) 2329 | 2330 | 2331 | # define widgets 2332 | WLPath = window.findChild(QtWidgets.QLabel, "LPath") 2333 | WPackedPath = window.findChild(QtWidgets.QLineEdit, "PackedPath") 2334 | WGoToRootFolder = window.findChild(QtWidgets.QPushButton, "GoToRootFolder") 2335 | WChoosePackedPathButton = window.findChild(QtWidgets.QPushButton, "ChoosePackedPathButton") 2336 | 2337 | WSendToIgnore = window.findChild(QtWidgets.QPushButton, "SendToIgnore") 2338 | WSendToCopy = window.findChild(QtWidgets.QPushButton, "SendToCopy") 2339 | WRefresh = window.findChild(QtWidgets.QPushButton, "Refresh") 2340 | WGoToFolder = window.findChild(QtWidgets.QPushButton, "GoToFolder") 2341 | WGoToNode = window.findChild(QtWidgets.QPushButton, "GoToNode") 2342 | WListCopyPaths = window.findChild(QtWidgets.QListWidget, "ListCopyPaths") 2343 | WListIgnorePaths = window.findChild(QtWidgets.QListWidget, "ListIgnorePaths") 2344 | 2345 | WRelinkPaths = window.findChild(QtWidgets.QCheckBox, "RelinkPaths") 2346 | WRelativeRelink = window.findChild(QtWidgets.QCheckBox, "RelativeRelink") 2347 | WNodeNameFolder = window.findChild(QtWidgets.QCheckBox, "NodeNameFolder") 2348 | WParentDirectories = window.findChild(QtWidgets.QSpinBox, "ParentDirectories") 2349 | WLParentDirectories = window.findChild(QtWidgets.QLabel, "LParentDirectories") 2350 | 2351 | WCopyFontDir = window.findChild(QtWidgets.QCheckBox, "CopyFontDir") 2352 | WCopyGizmos = window.findChild(QtWidgets.QCheckBox, "CopyGizmos") 2353 | 2354 | WContinueOnError = window.findChild(QtWidgets.QCheckBox, "ContinueOnError") 2355 | WExitOnFinish = window.findChild(QtWidgets.QCheckBox, "ExitOnFinish") 2356 | WCSVSeparator = window.findChild(QtWidgets.QComboBox, "CSVSeparator") 2357 | WLCSVSeparator = window.findChild(QtWidgets.QLabel, "LCSVSeparator") 2358 | WLicense = window.findChild(QtWidgets.QComboBox, "License") 2359 | WLLicense = window.findChild(QtWidgets.QLabel, "LLicense") 2360 | 2361 | WIgnoredLabel = window.findChild(QtWidgets.QLabel, "IgnoredLabel") 2362 | WCurrItemPath = window.findChild(QtWidgets.QLabel, "CurrItemPath") 2363 | WPackedItemPath = window.findChild(QtWidgets.QLabel, "PackedItemPath") 2364 | WCurrItemFiles = window.findChild(QtWidgets.QLabel, "CurrItemFiles") 2365 | WCurrItemSize = window.findChild(QtWidgets.QLabel, "CurrItemSize") 2366 | 2367 | WCurrentCopyItem = window.findChild(QtWidgets.QLabel, "CurrentCopyItem") 2368 | WTotalProgress = window.findChild(QtWidgets.QProgressBar, "TotalProgress") 2369 | WItemProgress = window.findChild(QtWidgets.QProgressBar, "ItemProgress") 2370 | 2371 | WTotalCopySize = window.findChild(QtWidgets.QLabel, "TotalCopySize") 2372 | WStart = window.findChild(QtWidgets.QPushButton, "Start") 2373 | WInterrupt = window.findChild(QtWidgets.QPushButton, "Interrupt") 2374 | 2375 | WLWebsite = window.findChild(QtWidgets.QLabel, "LWebsite") 2376 | 2377 | 2378 | # connect widgets to functions 2379 | WPackedPath.textChanged.connect(lambda:PackedPathChanged()) 2380 | WGoToRootFolder.clicked.connect(lambda:GoToRootFolder()) 2381 | WChoosePackedPathButton.clicked.connect(lambda:ChoosePackedPath()) 2382 | 2383 | WSendToIgnore.clicked.connect(lambda:SendToIgnore()) 2384 | WSendToCopy.clicked.connect(lambda:SendToCopy()) 2385 | WRefresh.clicked.connect(lambda:Refresh()) 2386 | WGoToFolder.clicked.connect(lambda:OpenFolder()) 2387 | WGoToNode.clicked.connect(lambda:ToNode()) 2388 | WListCopyPaths.itemSelectionChanged.connect(lambda:CopyListSelectionChanged()) 2389 | WListIgnorePaths.itemSelectionChanged.connect(lambda:IgnoreListSelectionChanged()) 2390 | 2391 | WRelinkPaths.stateChanged.connect(lambda:RelinksInList(r = True)) 2392 | WRelativeRelink.stateChanged.connect(lambda:RelinksInList(rr = True)) 2393 | WNodeNameFolder.stateChanged.connect(lambda:UpdateItemInfo(n = True)) 2394 | WParentDirectories.valueChanged.connect(lambda:UpdateItemInfo(pd = True)) 2395 | 2396 | WCopyFontDir.stateChanged.connect(lambda:AddOnsInList(f = True)) 2397 | WCopyGizmos.stateChanged.connect(lambda:AddOnsInList(g = True)) 2398 | 2399 | WStart.clicked.connect(lambda:StartCopy()) 2400 | WInterrupt.clicked.connect(lambda:StopCopy()) 2401 | 2402 | 2403 | # disable esc 2404 | window.rejected.connect(lambda:exitForm()) 2405 | 2406 | # disable window resizing 2407 | window.setFixedSize(window.size()) 2408 | 2409 | # hide ui elements, set enable/disable state 2410 | ButtonsAllowed(True) 2411 | WIgnoredLabel.setVisible(False) 2412 | 2413 | 2414 | # show the UI window 2415 | window.show() 2416 | 2417 | # read comp data 2418 | Refresh() 2419 | 2420 | else: 2421 | 2422 | # open chosen nuke script 2423 | nuke.scriptOpen(nk) 2424 | 2425 | # non-threaded version of same function 2426 | data = RefreshThreaded() 2427 | return data 2428 | 2429 | 2430 | 2431 | # python function start 2432 | def WrapItUp(fromterminal = False, nk = '', startnow = False, out = '', nodenamefolder = True, parentdircount = 3, relinked = True, relativerelinked = True, media = True, skipdisablednodes = False, fonts = True, gizmos = True, csvcommas = False, licinteractive = False): 2433 | 2434 | global WIU_AppPath 2435 | 2436 | # set nuke app path if not running from terminal 2437 | if not fromterminal: 2438 | WIU_AppPath = nuke.EXE_PATH 2439 | 2440 | # if run from python function or terminal 2441 | if out is not '': 2442 | 2443 | # empty line 2444 | print('') 2445 | 2446 | # starting line 2447 | if fromterminal: 2448 | print('\n' + WIU_Log + 'Running from terminal (no UI)...') 2449 | else: 2450 | print('\n' + WIU_Log + 'Running from Python function (no UI)...') 2451 | 2452 | 2453 | # set to current nuke script if no custom one has been defined 2454 | if nk == '': 2455 | nk = nuke.scriptName() 2456 | nk = nk.replace('\\', '/') 2457 | 2458 | # error bool 2459 | err = False 2460 | 2461 | # check if out path exists, convert to backslashes 2462 | out = out.replace('\\', '/') 2463 | if not os.path.isdir(out): 2464 | err = True 2465 | print(WIU_Log + 'ERROR: Folder does not exist: ' + out) 2466 | 2467 | if not os.path.isfile(nk): 2468 | err = True 2469 | print(WIU_Log + 'ERROR: Nuke script does not exist: ' + nk) 2470 | 2471 | # check if parent directory count is a valid number 2472 | if parentdircount < 1 or parentdircount > 99: 2473 | err = True 2474 | print(WIU_Log + 'ERROR: directory count (' + str(parentdircount) + ') should be in range 1-99!') 2475 | 2476 | 2477 | # silent mode 2478 | silent = True 2479 | 2480 | 2481 | # exit on error 2482 | if err: 2483 | return 2484 | 2485 | # start 2486 | else: 2487 | 2488 | # param list 2489 | p = [['Nuke script\t', nk], 2490 | ['Output path\t', out], 2491 | ['Node name folders', nodenamefolder], 2492 | ['Parent dir count', parentdircount], 2493 | ['Relink\t', relinked], 2494 | ['Relink relative', relativerelinked], 2495 | ['media\t', media], 2496 | ['skipdisablednodes\t', skipdisablednodes], 2497 | ['Fonts\t', fonts], 2498 | ['Gizmo files\t', gizmos], 2499 | ['CSV commas\t', csvcommas], 2500 | ['Interactive license', licinteractive]] 2501 | 2502 | # param string 2503 | param = '\n' + WIU_Log + 'Selected parameters' 2504 | for i in p: 2505 | param += '\n' + WIU_Log + i[0] + '\t\t' + str(i[1]) 2506 | print(param) 2507 | 2508 | # only print preview 2509 | returnedFiles = _Start(silent, nk, False, out, nodenamefolder, parentdircount, relinked, relativerelinked, media, skipdisablednodes, fonts, gizmos, csvcommas, licinteractive) 2510 | returnedStr = '' 2511 | for i in returnedFiles: 2512 | returnedStr += '\n' + WIU_Log + str(i) 2513 | 2514 | print('\n' + WIU_Log + 'Found files (' + BytesToString(WIU_TotalSize) + '):\n' + returnedStr + '\n') 2515 | 2516 | 2517 | # if starting right away 2518 | if startnow: 2519 | 2520 | # start 2521 | print('\n' + WIU_Log + 'Starting...' + '\n') 2522 | 2523 | # begin 2524 | _Start(silent, nk, startnow, out, nodenamefolder, parentdircount, relinked, relativerelinked, media, skipdisablednodes, fonts, gizmos, csvcommas, licinteractive) 2525 | 2526 | 2527 | # if running with UI in Nuke 2528 | if out == '' and not fromterminal: 2529 | 2530 | # starting in UI mode w/o parameters 2531 | _Start() 2532 | 2533 | 2534 | 2535 | # autostart (if not imported) 2536 | if __name__ == "__main__": 2537 | 2538 | # get all args 2539 | c = nuke.rawArgs 2540 | 2541 | 2542 | # check if running from terminal 2543 | t = False 2544 | ti = False 2545 | try: 2546 | t = '-t' in c 2547 | ti = '-i' in c 2548 | 2549 | except Exception as e: 2550 | pass 2551 | 2552 | 2553 | # start with terminal commands 2554 | if t or ti: 2555 | 2556 | # error bool 2557 | err = False 2558 | 2559 | # get path 2560 | WIU_AppPath = c[0] 2561 | 2562 | # nukescript 2563 | aNK = '' 2564 | for index, arg in enumerate(c): 2565 | if arg in ['-nk'] and len(c) > index + 1: 2566 | aNK = c[index + 1] 2567 | del c[index] 2568 | del c[index] 2569 | break 2570 | 2571 | # destination path 2572 | aOut = '' 2573 | for index, arg in enumerate(c): 2574 | if arg in ['-o'] and len(c) > index + 1: 2575 | aOut = c[index + 1] 2576 | del c[index] 2577 | del c[index] 2578 | break 2579 | 2580 | # start (instead of only returning a to-do list) 2581 | aStart = False 2582 | for index, arg in enumerate(c): 2583 | if arg in ['-s']: 2584 | aStart = True 2585 | del c[index] 2586 | break 2587 | 2588 | # node name directory 2589 | aNodeName = True 2590 | for index, arg in enumerate(c): 2591 | if arg in ['-n']: 2592 | aNodeName = False 2593 | del c[index] 2594 | break 2595 | 2596 | # parent directory count 2597 | aDirCount = 3 2598 | for index, arg in enumerate(c): 2599 | if arg in ['-pd']: 2600 | try: 2601 | aDirCount = int(c[index + 1]) 2602 | except Exception as e: 2603 | err = True 2604 | print(WIU_Log + 'Non-numerical value entered for -pd.') 2605 | del c[index] 2606 | del c[index] 2607 | break 2608 | 2609 | # relinked 2610 | aReli = True 2611 | for index, arg in enumerate(c): 2612 | if arg in ['-r']: 2613 | aReli = False 2614 | del c[index] 2615 | break 2616 | 2617 | # relinked relative 2618 | aReliRela = True 2619 | for index, arg in enumerate(c): 2620 | if arg in ['-rr']: 2621 | aReliRela = False 2622 | del c[index] 2623 | break 2624 | 2625 | # media 2626 | aMedia = True 2627 | for index, arg in enumerate(c): 2628 | if arg in ['-m']: 2629 | aMedia = False 2630 | del c[index] 2631 | break 2632 | 2633 | # skip disabled nodes 2634 | aSkipDisabledNodes = False 2635 | for index, arg in enumerate(c): 2636 | if arg in ['-d']: 2637 | aSkipDisabledNodes = True 2638 | del c[index] 2639 | break 2640 | 2641 | # fonts 2642 | aFonts = True 2643 | for index, arg in enumerate(c): 2644 | if arg in ['-f']: 2645 | aFonts = False 2646 | del c[index] 2647 | break 2648 | 2649 | # gizmos 2650 | aGizmos = True 2651 | for index, arg in enumerate(c): 2652 | if arg in ['-g']: 2653 | aGizmos = False 2654 | del c[index] 2655 | break 2656 | 2657 | # csv commas 2658 | aCSV = False 2659 | for index, arg in enumerate(c): 2660 | if arg in ['-csvcommas']: 2661 | aCSV = True 2662 | del c[index] 2663 | break 2664 | 2665 | # use same license 2666 | aLicInteractive = ti 2667 | 2668 | 2669 | # error handling 2670 | if err: 2671 | print(WIU_Log + 'Usage:\n-nk (required)\n-o (required)\n-s (start now - if not, only a preview list of the files to be processed will be returned)\n-n (disable: place media in node name folder)\n-pd (default: 3)\n-r (disable: make relinked .nk)\n-rr (disable: make relative relinked .nk)\n-m (disable: collect media)\n-f (disable: collect font folder)\n-g (disable: collect gizmos)\n-csvcomma (use commas instead of semicolons as the CSV separator)') 2672 | else: 2673 | WrapItUp(fromterminal = True, nk = aNK, startnow = aStart, out = aOut, nodenamefolder = aNodeName, parentdircount = aDirCount, relinked = aReli, relativerelinked = aReliRela, media = aMedia, skipdisablednodes = aSkipDisabledNodes, fonts = aFonts, gizmos = aGizmos, csvcommas = aCSV, licinteractive = aLicInteractive) 2674 | 2675 | 2676 | else: 2677 | WrapItUp() -------------------------------------------------------------------------------- /WrapItUp/menu.py: -------------------------------------------------------------------------------- 1 | import WrapItUp 2 | nuke.menu('Nuke').addCommand('Extra/Wrap It Up', "WrapItUp.WrapItUp()") --------------------------------------------------------------------------------