· 6 years ago · Aug 14, 2019, 01:42 PM
1# Bastian Traunfellner / 2019
2#
3# GENERAL FILE OPERATIONS
4#
5#
6
7
8from Katana import NodegraphAPI, ScenegraphManager, UI4, CatalogManager, RenderManager, KatanaFile
9import os, time, platform, fnmatch, collections, time, sys, functools
10from PyQt4 import QtCore, QtGui
11from spoiler_widget import Spoiler
12import re
13from os import listdir
14from os.path import isfile, join
15
16class QHLine(QtGui.QFrame):
17 def __init__(self):
18 super(QHLine, self).__init__()
19 self.setFrameShape(QtGui.QFrame.HLine)
20 self.setFrameShadow(QtGui.QFrame.Sunken)
21
22
23
24# all widgets must be inside a class ending with (init_UI) otherwise it will not be found by the dynamic loader!
25class fileOpScriptsWidget_init_UI(QtGui.QWidget):
26 def __init__(self, rootPath, parent=None):
27 super(fileOpScriptsWidget_init_UI, self).__init__(parent=parent)
28 #MAIN
29 main_layout = QtGui.QVBoxLayout()
30 horizontalImportos = QtGui.QHBoxLayout()
31
32 self.currUser = os.getenv('USERNAME')
33 self.currSeq = os.getenv('AF_SEQUENCE_NAME')
34 self.currShot = os.getenv('AF_ENTITY_NAME')
35 self.currProj = os.getenv('AF_PROJECT_NAME')
36
37 #_________________________________________________________
38 #setup file table widget
39 self.fileTableLabel = QtGui.QLabel('Files:')
40 self.fileTopBtnLayout = QtGui.QHBoxLayout()
41 self.fileRefreshBtn = QtGui.QPushButton('refresh')
42 fileVerticalSpacer = QtGui.QSpacerItem(800, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
43 self.fileTopBtnLayout.addWidget(self.fileTableLabel)
44 self.fileTopBtnLayout.addItem(fileVerticalSpacer)
45 self.fileTopBtnLayout.addWidget(self.fileRefreshBtn)
46 self.fileRefreshBtn.clicked.connect(self.refreshFileTable)
47
48 self.fileTableWidget = QtGui.QTableWidget()
49 self.fileTableWidget.setColumnCount(2)
50 self.fileTableWidget.setAlternatingRowColors(True)
51 self.fileTableWidget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
52 self.fileTableWidget.installEventFilter(self)
53 self.fileTableWidget.setFocusPolicy(QtCore.Qt.NoFocus)
54 self.fileTableWidget.setHorizontalHeaderLabels(['name', 'date modified'])
55 self.fileTableWidget.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
56
57 self.fillFileTable()
58
59
60
61 #_________________________________________________________
62 #setup importomatic table
63 self.importomaticTableLabel = QtGui.QLabel('Importomatics:')
64 self.importoTopBtnLayout = QtGui.QHBoxLayout()
65 self.importoRefreshBtn = QtGui.QPushButton('refresh')
66 importoVerticalSpacer = QtGui.QSpacerItem(800, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
67 self.importoTopBtnLayout.addWidget(self.importomaticTableLabel)
68 self.importoTopBtnLayout.addItem(importoVerticalSpacer)
69 self.importoTopBtnLayout.addWidget(self.importoRefreshBtn)
70 self.importoRefreshBtn.clicked.connect(self.refreshImportomaticTable)
71
72 self.importomaticTableWidget = QtGui.QTableWidget()
73 self.importomaticTableWidget.setColumnCount(2)
74 self.importomaticTableWidget.setAlternatingRowColors(True)
75 self.importomaticTableWidget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
76 self.importomaticTableWidget.installEventFilter(self)
77 self.importomaticTableWidget.selectionModel().selectionChanged.connect(self.importomaticWidgetSelectionChanged)
78 self.importomaticTableWidget.setFocusPolicy(QtCore.Qt.NoFocus)
79 self.importomaticTableWidget.setHorizontalHeaderLabels(['status', 'importomatics'])
80
81 self.saveDefaultTemplateBtn = QtGui.QPushButton('save in template location')
82 self.updateCurrentImportomatic = QtGui.QPushButton('udpate current importomatic')
83 self.updateAllImportomatic = QtGui.QPushButton('update All Importomatics')
84 self.importomaticTableWidget.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
85
86 self.fillImportomaticTable()
87
88
89
90 #_________________________________________________________
91 #setup livegroup table
92 self.liveGrpTableLabel = QtGui.QLabel('liveGroups:')
93 self.liveGrpTopBtnLayout = QtGui.QHBoxLayout()
94 self.liveGrpRefreshBtn = QtGui.QPushButton('refresh')
95 livegrpVerticalSpacer = QtGui.QSpacerItem(800, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
96 self.liveGrpTopBtnLayout.addItem(livegrpVerticalSpacer)
97 self.liveGrpTopBtnLayout.addWidget(self.liveGrpTableLabel)
98 self.liveGrpTopBtnLayout.addWidget(self.liveGrpRefreshBtn)
99 self.liveGrpRefreshBtn.clicked.connect(self.refreshLiveGroup)
100
101 self.liveGrpTableWidget = QtGui.QTableWidget()
102 self.liveGrpTableWidget.setColumnCount(2)
103 self.liveGrpTableWidget.setAlternatingRowColors(True)
104 self.liveGrpTableWidget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
105 self.liveGrpTableWidget.installEventFilter(self)
106 self.liveGrpTableWidget.selectionModel().selectionChanged.connect(self.liveGrpWidgetSelectionChanged)
107 self.liveGrpTableWidget.setFocusPolicy(QtCore.Qt.NoFocus)
108 self.liveGrpTableWidget.setHorizontalHeaderLabels(['status', 'live group'])
109 self.liveGrpTableWidget.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
110
111 self.fillLiveGrpTable()
112
113
114
115
116 vbox = QtGui.QVBoxLayout()
117 vbox.setAlignment(QtCore.Qt.AlignTop)
118 spoilerBox = QtGui.QVBoxLayout()
119 main_layout.addLayout(vbox, 0)
120
121 self.spoiler = Spoiler(self, 'FILE OPS')
122
123 vbox.addWidget(self.spoiler)
124 main_layout.setAlignment(QtCore.Qt.AlignTop)
125
126
127 horizontalImportos.addWidget(self.updateCurrentImportomatic)
128 horizontalImportos.addWidget(self.updateAllImportomatic)
129 spoilerBox.addLayout(self.fileTopBtnLayout, 0)
130
131 spoilerBox.addWidget(self.fileTableWidget)
132 spoilerBox.addWidget(self.saveDefaultTemplateBtn)
133 spoilerBox.addWidget(QHLine())
134 spoilerBox.addLayout(self.importoTopBtnLayout,0)
135 spoilerBox.addWidget(self.importomaticTableWidget)
136
137 spoilerBox.addWidget(QHLine())
138 spoilerBox.addLayout(horizontalImportos, 0)
139
140 spoilerBox.addWidget(QHLine())
141 spoilerBox.addLayout(self.liveGrpTopBtnLayout,0)
142 spoilerBox.addWidget(self.liveGrpTableWidget)
143
144 self.saveDefaultTemplateBtn.clicked.connect(self.saveInDefaultLocation)
145 self.updateAllImportomatic.clicked.connect(self.updateAllImportomaticFunc)
146 self.updateCurrentImportomatic.clicked.connect(self.updateCurrentImportomaticFunc)
147 self.spoiler.setContentLayout(spoilerBox)
148 self.setLayout(main_layout)
149
150
151
152
153
154
155 #create importomatic and return the created node, mongo style from the menu
156 def createFullImporto(self):
157 menubar = None
158 for w in QtGui.qApp.topLevelWidgets():
159 if type(w).__name__ == 'LayoutsMenu':
160 menubar = w.parent()
161 for m in menubar.children():
162 try:
163 for a in m.actions():
164 if a.text() == 'Create Full Importomatic':
165 a.trigger()
166 except:
167 pass
168
169
170
171
172
173
174 #update the selected importomatic from the imporotomatic table widgets rightclick menu
175 def updateCurrentImportomaticFunc(self, importomatic):
176 curr_seq = os.getenv('AF_SEQUENCE_NAME')
177 curr_shot = os.getenv('AF_ENTITY_NAME')
178
179
180 m = None
181 #fetch all importomatics and filter by current shot
182 selNode = [x for x in NodegraphAPI.GetAllNodesByType('Importomatic') if x.getName() == importomatic]
183 if len(selNode) != 0:
184 #get numbers in shot name
185 m = re.search(r'\d+$', selNode[0].getName())
186 selNode = selNode[0]
187 name = selNode.getAttributes()['name']
188 #if a node is selected and it's an importomatic and it's name is an approximation of the current shotsname take this one
189 '''
190 if m:
191 if len(selNode) != 0 and selNode[0].getType() == 'Importomatic' and selNode[0].getName().startswith(self.currShot.split('_')[0]+'_'+self.currShot.split('_')[1][:-1]) or str(m.group()) in self.currShot:
192 importomatic = selNode[0]
193 name = self.currShot
194
195 #importomatic with regular name exists just grab that.
196 else:
197 return
198 importomatic = [x for x in NodegraphAPI.GetAllNodesByType('Importomatic') if fnmatch.fnmatch(x.getName(), curr_shot)]
199 #importomatic exists
200 if importomatic and len(importomatic)!=0:
201 importomatic = importomatic[0]
202 else:
203 importomatic = NodegraphAPI.GetAllSelectedNodes()[0]
204
205 name = importomatic.getAttributes()['name']
206 '''
207
208 #get Importomatic position and output connection info
209 posX = selNode.getAttributes()['x']
210 posY = selNode.getAttributes()['y']
211
212
213 connNode = selNode.getOutputPortByIndex(0).getConnectedPort(0).getNode()
214 connInput = selNode.getOutputPortByIndex(0).getConnectedPort(0)
215
216 #create new Importomatic
217 self.createFullImporto()
218 newImportomatic = NodegraphAPI.GetAllSelectedNodes()[-1]
219 #deelete old importomatic
220 selNode.delete()
221
222 #transfer data.
223 #newImportomatic.getAttributes()['x'].setValue(posX, 0)
224 #newImportomatic.getAttributes()['y'].setValue(posY, 0)
225 NodegraphAPI.SetNodePosition(newImportomatic, (posX, posY))
226 newImportomatic.getOutputPortByIndex(0).connect(connInput)
227 newImportomatic.setName(str(name), 0)
228
229 self.refreshImportomaticTable()
230
231
232
233 #update catalog selection from the catalog2 selection changed
234 def importomaticWidgetSelectionChanged(self):
235
236 #match selection of the local file snapshot list.
237 normDateIn = None
238 currRow = [x.row() for x in self.importomaticTableWidget.selectedIndexes()]
239
240 #the [-1] is the position of the label which holds the date. try -2 for name of layer and so forth
241 if not currRow:
242 return
243 #get column of date by text
244 currItem = self.importomaticTableWidget.item(currRow[0], 1).text()
245 selNode = [x for x in NodegraphAPI.GetAllNodesByType('Importomatic') if x.getName() == currItem]
246
247 self.ngSelectAndFocus(selNode[0])
248
249
250
251 def liveGrpWidgetSelectionChanged(self):
252 #match selection of the local file snapshot list.
253 normDateIn = None
254 currRow = [x.row() for x in self.liveGrpTableWidget.selectedIndexes()]
255
256 #the [-1] is the position of the label which holds the date. try -2 for name of layer and so forth
257 if not currRow:
258 return
259 #get column of date by text
260 currItem = self.liveGrpTableWidget.item(currRow[0], 1).text()
261 selNode = [x for x in NodegraphAPI.GetAllNodesByType('LiveGroup') if x.getName() == currItem]
262
263 self.ngSelectAndFocus(selNode[0])
264
265
266
267 def updateAllImportomaticFunc(self):
268 print 'test'
269
270
271
272
273 #get file creation date for fileops
274 def creation_date(self, path_to_file):
275 if platform.system() == 'Windows':
276 return os.path.getctime(path_to_file)
277 else:
278 stat = os.stat(path_to_file)
279 try:
280 return stat.st_birthtime
281 except AttributeError:
282 # We're probably on Linux. No easy way to get creation dates here,
283 # so we'll settle for when its content was last modified.
284 return stat.st_mtime
285
286 def to_datetime(self, input):
287 return(time.ctime(input) )
288
289
290
291
292
293 #what's the latest file created on one sequence sorted by user.
294 def getLatestFileFromSequence(self):
295
296 fileName = NodegraphAPI.GetKatanaSceneName() + '.katana'
297 filePath = NodegraphAPI.GetProjectDir()
298
299 if not filePath:
300 filePath = '/Volumes/af/show/{0}/shots/{1}/{2}/sandbox/{3}/katana/scenes'.format(self.currProj, self.currSeq, self.currShot, self.currUser)
301
302 fpAfterSht = '/'.join(filePath.split(self.currSeq)[2].split('/')[1:])
303 fpBeforeSht = filePath.split(self.currSeq)[0] + self.currSeq
304
305 #matches = collections.OrderedDict()
306 matches = {}
307 for x in range(0, 1000, 10):
308 #print filePath.split(currShot)[0] + currSeq + '_' + str("{:04d}".format(x)) + filePath.split(currShot)[1] + '/'
309 #print fpBeforeSht + '/' + self.currSeq + '_' + str("{:04d}".format(x)) + '/' + fpAfterSht + '/'
310 for root, dirnames, filenames in os.walk(fpBeforeSht + '/' + self.currSeq + '_' + str("{:04d}".format(x)) + '/' + fpAfterSht + '/'):
311 for filename in fnmatch.filter(filenames, '*.katana'):
312 fp = os.path.join(root, filename)
313 matches[self.creation_date ( fp )] = os.path.join( fp )
314
315 #sorted(matches.items(), key = lambda kvp: self.to_datetime(kvp[0]))
316 return sorted(matches.items())
317
318
319
320 #check if the given importomatic is out of date.
321 def checkImportomaticState(self, importomatic):
322 dirty = False
323 pythonTab = UI4.App.Tabs.FindTopTab('Python')
324 #_______________________CUSTOM SHOT INFO THAT IS SHOWN ON TOP OF THE TAB --> in this case what assets are in the scene.
325 shotName = os.getenv('AF_ENTITY_NAME')
326 inGeos = [x for x in NodegraphAPI.GetAllNodes() if x.getType() == 'Alembic_In' or x.getType() == 'af_ScenegraphXml_In']
327 if inGeos and len(inGeos) != 0:
328 for x in inGeos:
329 #these are abcAssets, only for camera?
330 if x.getParameters().getChild('abcAsset'):
331 fp = x.getParameters().getChild('abcAsset').getValue(0)
332 inputAlembic = os.path.basename(fp).split('.abc')[0]
333
334 pathVersion = [i for i in fp.split('_') if i.startswith('v')]
335 #filter out only current shots
336 if inputAlembic.startswith(shotName):
337 try:
338 if inputAlembic.endswith('camera') or inputAlembic.endswith('geo'):
339 #get the camera
340 baseVersion = os.path.basename(fp).split('_')[-4]
341 combName = inputAlembic.split('v')[0].rstrip('_')
342 #get regular assets
343 else:
344 baseVersion = os.path.basename(fp).split('_')[-8]
345 combName = inputAlembic.split(inputAlembic.split('v00')[0].split('_')[:-1][-1])[0]
346 vUp = str('v' + str(int(pathVersion[0].split('v')[1]) + 1).zfill(len(pathVersion[0].split('v')[1])))
347 except:
348 pass
349
350
351 alemSplit = inputAlembic.split('_')
352 if os.path.exists( os.path.dirname(fp.replace(pathVersion[0], vUp)) ) :
353 dirty = True
354
355
356 #two types of assets, asset or child. this is asset
357 if x.getParameters().getChild('asset'):
358 fp = x.getParameters().getChild('asset').getValue(0)
359
360 #get version, with failsave to compare with version in basename in case some other letter starts with v
361 pathVersion = [l for l in fp.split('_') if l.startswith('v')]
362 baseVersion = os.path.basename(fp).split('_')[-3]
363 inputAlembic = os.path.basename(fp).split('.abc')[0]
364
365 #find index of version in filename and replace it with one version up
366 #version up
367 vUp = str('v' + str(int(pathVersion[0].split('v')[1]) + 1).zfill(len(pathVersion[0].split('v')[1])))
368
369 if os.path.exists( os.path.dirname(fp.replace(pathVersion[0], vUp)) ) and pathVersion[0] == baseVersion:
370 #try this if fails with doulbe v00 in split
371 dirty = True
372
373 return dirty
374
375 ##refresh importomatic widget
376 def refreshImportomaticTable(self):
377 self.importomaticTableWidget.setRowCount(0)
378 self.fillImportomaticTable()
379
380
381 #fill importomatic widget by what importomatics are available.
382 def fillImportomaticTable(self):
383 allImportos = [x for x in NodegraphAPI.GetRootNode().getChildren() if x.getType() == 'Importomatic']
384 #print len(allImportos)
385 if allImportos:
386 self.importomaticTableWidget.setRowCount(len(allImportos))
387 for i in range(0,len(allImportos)):
388 dirty = self.checkImportomaticState(allImportos[i])
389 if dirty == True:
390 self.importomaticTableWidget.setItem(i, 0, QtGui.QTableWidgetItem('..outdated') )
391 else:
392 self.importomaticTableWidget.setItem(i, 0, QtGui.QTableWidgetItem('ok') )
393
394 self.importomaticTableWidget.setItem(i, 1, QtGui.QTableWidgetItem(allImportos[i].getName()) )
395
396
397
398 #focus on given node
399 def ngSelectAndFocus(self, node):
400 #select the node 'item'
401 NodegraphAPI.SetAllSelectedNodes('')
402
403 NodegraphAPI.SetNodeSelected(node, True)
404 #frame on the selected node
405 self.frameSelected()
406 NodegraphAPI.SetAllSelectedNodes('')
407 NodegraphAPI.SetNodeSelected(node, True)
408
409
410 def frameSelected(self):
411 UI4.App.Tabs.FindTopTab('Node Graph')._NodegraphPanel__goMenuActions['frameselection'].trigger()
412
413
414
415 #fill the filename table
416 def fillFileTable(self):
417 fileSeq = self.getLatestFileFromSequence()
418 if not fileSeq:
419 print 'could not retrieve project path, please make sure you are jobbed in correctly'
420 return
421 file = list(reversed(fileSeq))
422 self.fileTableWidget.setRowCount(len(file))
423
424 for f in enumerate(file):
425 col1 = os.path.basename(f[1][1])
426 col2 = time.ctime(f[1][0])
427 #fill col0
428 self.fileTableWidget.setItem(f[0], 0, QtGui.QTableWidgetItem(f[1][1].split('/')[7] + ' / ' + os.path.basename(f[1][1])) )
429 #fill col1
430 self.fileTableWidget.setItem(f[0], 1, QtGui.QTableWidgetItem(col2) )
431
432 #color all rows red that the sequence template file doesn't match to the 0000 directory
433 if 'sequenceTemplate' in os.path.basename(f[1][1]) and re.search(r'\d+$', f[1][1].split('/')[7]).group() != '0000':
434 for j in range(self.fileTableWidget.columnCount()):
435 self.fileTableWidget.item(f[0], j).setBackground(QtGui.QColor(255,0,0))
436
437
438 #reload the file load table widget.
439 def refreshFileTable(self):
440 self.fileTableWidget.setRowCount(0)
441 self.fillFileTable()
442
443
444 #all event filters for all the tabel widgets
445 def eventFilter(self, source, event):
446 if not event or not source or not QtGui:
447 return
448 #install rightclick event filter for file open dialog widget
449 if (event.type() == QtCore.QEvent.ContextMenu and source is self.fileTableWidget):
450 menu = QtGui.QMenu()
451 menu.addAction('open')
452 if menu.exec_(event.globalPos()):
453 item = source.itemAt(event.pos())
454 self.openFile(self.fileTableWidget.currentItem().text())
455 return True
456 #install rightclick event filter for importomatic widget
457 elif (event.type() == QtCore.QEvent.ContextMenu and source is self.importomaticTableWidget):
458 menu = QtGui.QMenu()
459 menu.addAction('update')
460 if menu.exec_(event.globalPos()):
461 row = source.rowAt(event.pos().y()-22)
462 self.updateCurrentImportomaticFunc(self.importomaticTableWidget.item(row, 1).text())
463 return True
464 #install rightclick event filter for livegrouup widget
465 elif (event.type() == QtCore.QEvent.ContextMenu and source is self.liveGrpTableWidget):
466 menu = QtGui.QMenu()
467 menu.addAction('update')
468 if menu.exec_(event.globalPos()):
469 row = source.rowAt(event.pos().y()-22)
470 self.updateCurrentLiveGroupFunc(self.liveGrpTableWidget.item(row, 1).text())
471 return True
472 else:
473 return super(fileOpScriptsWidget_init_UI, self).eventFilter(source, event)
474
475
476 #opens the katana file from the fileTableWidget.
477 def openFile(self, row):
478 filePath = NodegraphAPI.GetProjectDir()
479 if not filePath:
480 filePath = '/Volumes/af/show/{0}/shots/{1}/{2}/sandbox/{3}/katana/scenes'.format(self.currProj, self.currSeq, self.currShot, self.currUser)
481 shot = row.split(' /')[0]
482 filename = row.split('/ ')[1]
483 finalFileName = filePath.replace(filePath.split('/')[7], shot) + '/' + filename
484 KatanaFile.Load(str(finalFileName), isCrashFile=False)
485
486
487
488
489 #just a versioning function to increment versions
490 def getAndCreateLatestVersion(self, input):
491 objects = []
492 myDict = {}
493 nmbSplit = [re.findall(r'(\w+?)(\d+)', x)[0] for x in input if x[-1].isdigit()]
494
495 if nmbSplit:
496 for name, value in nmbSplit:
497 if not myDict.has_key(name):
498 myDict[name] = []
499
500 myDict[name].append(value)
501
502 #maximum = max(myDict, key=myDict.get)
503
504 #return myDict[maximum][0]
505 max_value = max(myDict.values())
506
507 return max(max_value)
508
509 else:
510 return 1
511
512
513
514 #save the katana file in teh 0000 directory of the current sequence.
515 def saveInDefaultLocation(self):
516 #determine if file is already saved
517 fileName = NodegraphAPI.GetKatanaSceneName() + '.katana'
518 filePath = NodegraphAPI.GetProjectDir()
519
520 if not filePath:
521 filePath = '/Volumes/af/show/{0}/shots/{1}/{2}/sandbox/{3}/katana/scenes'.format(self.currProj, self.currSeq, self.currShot, self.currUser)
522
523 defaultPath = filePath.replace(filePath.split('/')[7], self.currSeq+'_0000')
524 onlyfiles = [f for f in listdir(defaultPath) if isfile(join(defaultPath, f))]
525
526
527 #THIS file is not yet saved, save with default filename
528 if fileName.startswith('.katana'):
529 #now check if the folder already contains sequenceTemplate files if so query version and version up.
530 if len(onlyfiles) != 0:
531 matchFiles = []
532 for file in onlyfiles:
533 if fnmatch.fnmatch(file, '*sequenceTemplate*'):
534 matchFiles.append(file.split('.katana')[0])
535
536 #if there's already scenees with sequenceTemplate just find the highest version and version up.
537 if matchFiles:
538 #if leading zero, maintain it.
539 maxVers = self.getAndCreateLatestVersion(matchFiles)
540 newVers = None
541 if maxVers.startswith('0'):
542 newVers ='%02d'% int(int(maxVers)+1)
543 elif maxVers.startswith('00'):
544 newVers ='%03d'% int(int(maxVers)+1)
545
546 #now get the latest file and append newVers, with check if new version exists.
547 newFileName = sorted(matchFiles)[-1].replace(maxVers, newVers) + '.katana'
548 finalPath = filePath.replace(filePath.split('/')[7], self.currSeq)+'_0000'+'/'+str(newFileName)
549 if not os.path.isfile(finalPath):
550 KatanaFile.Save(finalPath, extraOptionsDict=None)
551 else:
552 print 'destination file: %s exists, aborted'%newFileName
553
554 else:
555 #else can create a new one
556 #redundancy check if file really doesn't exist
557 finalPath = filePath.replace(filePath.split('/')[7], self.currSeq+'_0000')+'/'+self.currShot+'_sequenceTemplate_v01.katana'
558 if not os.path.isfile(finalPath):
559 KatanaFile.Save(finalPath)
560 else:
561 print('destination file: {0} exists, aborted').format(newFileName)
562
563 else:
564 #creeate new file
565 #redundancy check if file really doesn't exist
566 finalPath = filePath.replace(filePath.split('/')[7], self.currSeq+'_0000')+'/'+self.currShot+'_sequenceTemplate_v01.katana'
567 if not os.path.isfile(finalPath):
568 KatanaFile.Save(finalPath)
569 else:
570 print('destination file: {0} exists, aborted').format(newFileName)
571
572 else:
573 #else just iterate up the current file name version
574 clnFileName = fileName.split('.katana')[0]
575 m = re.search(r'\d+$', clnFileName)
576 # if the string ends in digits m will be a Match object, or None otherwise.
577 if m is not None:
578 vUp = int(m.group())+1
579 if m.group().startswith('0'):
580 vUp ='%02d'%int(int(m.group())+1)
581 elif m.group().startswith('00'):
582 vUp ='%03d'%int(int(m.group())+1)
583 newFileName = clnFileName.replace(m.group(), str(vUp)) + '.katana'
584 else:
585 newFileName = clnFileName + '_v01.katana'
586
587
588 #redundancy check if the folder has maybe later scene files than the current one.
589 if len(onlyfiles) != 0:
590 matchFiles = []
591 for file in onlyfiles:
592 #strip number and fileextension and fnmatch only on names containing that.
593 if fnmatch.fnmatch(file, str(clnFileName.split(str(m.group()))[0])+'*'):
594 matchFiles.append(file.split('.katana')[0])
595
596
597 #now get latest
598 highestVersion = self.getAndCreateLatestVersion(matchFiles)
599 #there;s a higher version in the versions folder print warning!
600 if highestVersion > vUp:
601 finalPath = filePath.replace(filePath.split('/')[7], self.currSeq+'_0000')+'/'+str(newFileName)
602 if not os.path.isfile(finalPath):
603 KatanaFile.Save(finalPath)
604 else:
605 print('destination file: {0} exists, aborted').format(newFileName)
606 KatanaFile.Save(finalPath)
607
608 self.refreshFileTable()
609
610
611
612
613
614 def fillLiveGrpTable(self):
615 liveGrps = [x for x in NodegraphAPI.GetAllNodesByType('LiveGroup')]
616
617 if not liveGrps:
618 print 'could not retrieve project path, please make sure you are jobbed in correctly'
619 return
620 self.liveGrpTableWidget.setRowCount(len(liveGrps))
621
622 for f in enumerate(liveGrps):
623 datedCheck = self.outdatedLiveGroupsCheck(f[1])
624 print f[1], datedCheck
625 #means it's outdated, color red.
626 if datedCheck == True:
627 col1 = 'outdated!'
628 col2 = f[1].getName()
629 self.liveGrpTableWidget.setItem(f[0], 1, QtGui.QTableWidgetItem(col2))
630 self.liveGrpTableWidget.setItem(f[0], 0, QtGui.QTableWidgetItem(col1))
631 else:
632 col1 = 'ok'
633 col2 = f[1].getName()
634 self.liveGrpTableWidget.setItem(f[0], 1, QtGui.QTableWidgetItem(col2))
635 self.liveGrpTableWidget.setItem(f[0], 0, QtGui.QTableWidgetItem(col1))
636
637
638
639 for f in enumerate(liveGrps):
640 #now color after rows are setup
641 datedCheck = self.outdatedLiveGroupsCheck(f[1])
642 if datedCheck:
643 for j in range(self.liveGrpTableWidget.columnCount()):
644 self.liveGrpTableWidget.item(f[0], j).setBackground(QtGui.QColor(255,0,0))
645
646
647 def refreshLiveGroup(self):
648 self.liveGrpTableWidget.setRowCount(0)
649 self.fillLiveGrpTable()
650
651
652
653 def outdatedLiveGroupsCheck(self, liveGroup):
654 fp = liveGroup.getParameter('source').getValue(0)
655
656 #get the version if the version starts with v, has length min 4 chars, and has number after the v
657 if not 'live' in fp.split('/'):
658 getVersion = [x for x in fp.split('/') if x.startswith('v') and len(x)>=4 and re.search(r'\d+$', x.split('v')[1])]
659
660 vUp = 'v'+str(int(getVersion[0].split('v')[1])+1).zfill(len(getVersion[0].split('v')[1]))
661 if os.path.exists(fp.replace(getVersion[0], vUp)):
662 return True
663 else:
664 return False
665
666
667 #just a versioning function to increment versions
668 def getAndCreateLatestVersion(self, input):
669 objects = []
670 myDict = {}
671 nmbSplit = [re.findall(r'(\w+?)(\d+)', x)[0] for x in input if x[-1].isdigit()]
672
673 if nmbSplit:
674 for name, value in nmbSplit:
675 if not myDict.has_key(name):
676 myDict[name] = []
677
678 myDict[name].append(value)
679
680 #maximum = max(myDict, key=myDict.get)
681
682 #return myDict[maximum][0]
683 max_value = max(myDict.values())
684
685 return max(max_value)
686
687 else:
688 return 1
689
690
691
692
693
694 def updateCurrentLiveGroupFunc(self, liveGroup):
695 '''
696 liveGroupByName = [x for x in NodegraphAPI.GetAllNodesByType('LiveGroup') if x.getName() == liveGroup]
697 fp = liveGroupByName[0].getParameter('source').getValue(0)
698
699 #redundancy check
700 if os.path.dirname(fp).split('/')[-1]=='export':
701 dirFolder = '/'.join(os.path.dirname(fp).split('/')[:-2])+'/'
702 versionFolders = [x for x in os.listdir(dirFolder)]
703 maxVersion = self.getAndCreateLatestVersion(versionFolders)
704 getVersion = [x for x in fp.split('/') if x.startswith('v') and len(x)>=4 and re.search(r'\d+$', x.split('v')[1])]
705
706 #set new livegroup path
707 liveGroupByName[0].getParameter('source').setValue(fp.replace(str(getVersion[0]), 'v'+str(maxVersion)), 0)
708
709 self.refreshLiveGroup()
710 '''
711 selNode = [x for x in NodegraphAPI.GetAllNodesByType('LiveGroup') if x.getName() == liveGroup]
712 m = re.search(r'\d+$', selNode[0].getName())
713 selNode = selNode[0]
714 name = selNode.getAttributes()['name']
715
716
717 #get Importomatic position and output connection info
718 posX = selNode.getAttributes()['x']
719 posY = selNode.getAttributes()['y']
720
721
722 connNode = selNode.getOutputPortByIndex(0).getConnectedPort(0).getNode()
723 connInput = selNode.getOutputPortByIndex(0).getConnectedPort(0)
724 connOutput = selNode.getInputPortByIndex(0).getConnectedPort(0)
725
726 #get latest livegroup
727 fp = selNode.getParameter('source').getValue(0)
728
729 #redundancy check
730 if os.path.dirname(fp).split('/')[-1]=='export':
731 dirFolder = '/'.join(os.path.dirname(fp).split('/')[:-2])+'/'
732 versionFolders = [x for x in os.listdir(dirFolder)]
733 maxVersion = self.getAndCreateLatestVersion(versionFolders)
734 getVersion = [x for x in fp.split('/') if x.startswith('v') and len(x)>=4 and re.search(r'\d+$', x.split('v')[1])]
735 if maxVersion:
736 newFp = fp.replace(str(getVersion[0]), 'v'+str(maxVersion))
737
738 if not newFp:
739 return
740 importPath = newFp
741
742
743 #import new livegroup
744 newImportGrp = KatanaFile.Import(importPath)[0]
745
746
747 #deelete old importomatic
748 selNode.delete()
749
750 #transfer data.
751 NodegraphAPI.SetNodePosition(newImportGrp, (posX, posY))
752 newImportGrp.getOutputPortByIndex(0).connect(connInput)
753 newImportGrp.getInputPortByIndex(0).connect(connOutput)
754 newImportGrp.setName(str(name), 0)
755
756 rootParam = newImportGrp.getParameters()
757 sourceLabel = rootParam.createChildString('source', newFp)
758
759 self.refreshLiveGroup()