├── 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 | 
3 |
4 | 
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 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