20
20
from __future__ import print_function
21
21
from __future__ import division
22
22
23
-
23
+ from future . utils import iteritems
24
24
from builtins import map
25
25
import time
26
26
@@ -71,6 +71,7 @@ class JobMonitorTree(cuegui.AbstractTreeWidget.AbstractTreeWidget):
71
71
"""Tree widget to display a list of monitored jobs."""
72
72
73
73
__loadMine = True
74
+ __groupDependent = True
74
75
view_object = QtCore .Signal (object )
75
76
76
77
def __init__ (self , parent ):
@@ -151,7 +152,9 @@ def __init__(self, parent):
151
152
152
153
self .__jobTimeLoaded = {}
153
154
self .__userColors = {}
154
-
155
+ self .__dependentJobs = {}
156
+ self ._dependent_items = {}
157
+ self .__reverseDependents = {}
155
158
# Used to build right click context menus
156
159
self .__menuActions = cuegui .MenuActions .MenuActions (
157
160
self , self .updateSoon , self .selectedObjects )
@@ -180,8 +183,21 @@ def tick(self):
180
183
self ._update ()
181
184
return
182
185
186
+ self .updateJobCount ()
183
187
self .ticksWithoutUpdate += 1
184
188
189
+ def updateJobCount (self ):
190
+ """Called at every tick. The total number of monitored
191
+ jobs is added to the column header
192
+ """
193
+ count = 0
194
+ iterator = QtWidgets .QTreeWidgetItemIterator (self )
195
+ while iterator .value ():
196
+ count += 1
197
+ iterator += 1
198
+
199
+ self .headerItem ().setText (0 , "Job [Total Count: {}]" .format (count ))
200
+
185
201
def __itemSingleClickedCopy (self , item , col ):
186
202
"""Called when an item is clicked on. Copies selected object names to
187
203
the middle click selection clip board.
@@ -226,13 +242,21 @@ def setLoadMine(self, value):
226
242
@type value: boolean or QtCore.Qt.Checked or QtCore.Qt.Unchecked"""
227
243
self .__loadMine = (value is True or value == QtCore .Qt .Checked )
228
244
229
- def addJob (self , job , timestamp = None ):
245
+ def setGroupDependent (self , value ):
246
+ """Enables or disables the auto grouping of the dependent jobs
247
+ @param value: New groupDependent state
248
+ @type value: boolean or QtCore.Qt.Checked or QtCore.Qt.Unchecked"""
249
+ self .__groupDependent = (value is True or value == QtCore .Qt .Checked )
250
+ self .updateRequest ()
251
+
252
+ def addJob (self , job , timestamp = None , loading_from_config = False ):
230
253
"""Adds a job to the list. With locking"
231
254
@param job: Job can be None, a job object, or a job name.
232
255
@type job: job, string, None
233
- @param timestamp: UTC time of the specific date the job was
234
- added to be monitored
235
- @type timestamp: float"""
256
+ @param loading_from_config: Whether or not this method is being called
257
+ for loading jobs found in user config
258
+ @type loading_from_config: bool
259
+ """
236
260
newJobObj = cuegui .Utils .findJob (job )
237
261
self .ticksLock .lock ()
238
262
try :
@@ -241,6 +265,42 @@ def addJob(self, job, timestamp=None):
241
265
if not self .__groupDependent :
242
266
self .__load [jobKey ] = newJobObj
243
267
self .__jobTimeLoaded [jobKey ] = timestamp if timestamp else time .time ()
268
+ else :
269
+ # We'll only add the new job if it's not already listed
270
+ # as a dependent on another job
271
+ if jobKey not in self .__reverseDependents .keys ():
272
+ self .__load [jobKey ] = newJobObj
273
+
274
+ # when we are adding jobs manually, we want to calculate
275
+ # all dependencies (active or not), so the user can see
276
+ # all the dependent jobs, even after the main/parent job
277
+ # has finished.
278
+ # When we're loading jobs from user config, we want to
279
+ # only include the active dependents. This is because
280
+ # the dependencies have already been calculated and
281
+ # listed in the config as a flat list, so attempting
282
+ # to re-add them will result in duplicates that will
283
+ # throw off the cleanup loop at the end of this method
284
+ active_only = not loading_from_config
285
+ dep = self .__menuActions .jobs (
286
+ ).getRecursiveDependentJobs ([newJobObj ],
287
+ active_only = active_only )
288
+ self .__dependentJobs [jobKey ] = dep
289
+ # we'll also store a reversed dictionary for
290
+ # dependencies with the dependent as key and the main
291
+ # job as the value, this will be used in step 2
292
+ # below to remove jobs that are added here
293
+ # as dependents
294
+ for j in dep :
295
+ depKey = cuegui .Utils .getObjectKey (j )
296
+ self .__reverseDependents [depKey ] = newJobObj
297
+ self .__jobTimeLoaded [depKey ] = time .time ()
298
+ self .__jobTimeLoaded [jobKey ] = time .time ()
299
+
300
+ for j in self .__reverseDependents :
301
+ if j in self .__load :
302
+ del self .__load [j ]
303
+
244
304
finally :
245
305
self .ticksLock .unlock ()
246
306
@@ -274,6 +334,20 @@ def _removeItem(self, item):
274
334
# pylint: enable=no-member
275
335
cuegui .AbstractTreeWidget .AbstractTreeWidget ._removeItem (self , item )
276
336
self .__jobTimeLoaded .pop (item .rpcObject , "" )
337
+ try :
338
+ jobKey = cuegui .Utils .getObjectKey (item )
339
+ # Remove the item from the main _items dictionary as well as the
340
+ # __dependentJobs and the reverseDependent dictionaries
341
+ cuegui .AbstractTreeWidget .AbstractTreeWidget ._removeItem (self , item )
342
+ dependent_jobs = self .__dependentJobs .get (jobKey , [])
343
+ for djob in dependent_jobs :
344
+ del self .__reverseDependents [djob ]
345
+ del self .__reverseDependents [jobKey ]
346
+ except KeyError :
347
+ # Dependent jobs are not stored in as keys the main self._items
348
+ # dictionary, trying to remove dependent jobs from self._items
349
+ # raises a KeyError, which we can safely ignore
350
+ pass
277
351
278
352
def removeAllItems (self ):
279
353
"""Notifies the other widgets of each item being unmonitored, then calls
@@ -284,6 +358,8 @@ def removeAllItems(self):
284
358
# pylint: enable=no-member
285
359
if proxy in self .__jobTimeLoaded :
286
360
del self .__jobTimeLoaded [proxy ]
361
+ self .__dependentJobs .clear ()
362
+ self .__reverseDependents .clear ()
287
363
cuegui .AbstractTreeWidget .AbstractTreeWidget .removeAllItems (self )
288
364
289
365
def removeFinishedItems (self ):
@@ -296,6 +372,7 @@ def contextMenuEvent(self, e):
296
372
@param e: Right click QEvent
297
373
@type e: QEvent"""
298
374
menu = QtWidgets .QMenu ()
375
+ menu .setToolTipsVisible (True )
299
376
300
377
__selectedObjects = self .selectedObjects ()
301
378
__count = len (__selectedObjects )
@@ -304,6 +381,7 @@ def contextMenuEvent(self, e):
304
381
self .__menuActions .jobs ().addAction (menu , "unmonitor" )
305
382
self .__menuActions .jobs ().addAction (menu , "view" )
306
383
self .__menuActions .jobs ().addAction (menu , "emailArtist" )
384
+ self .__menuActions .jobs ().addAction (menu , "showProgBar" )
307
385
self .__menuActions .jobs ().addAction (menu , "viewComments" )
308
386
self .__menuActions .jobs ().addAction (menu , "useLocalCores" )
309
387
@@ -404,11 +482,21 @@ def _getUpdate(self):
404
482
# Gather list of all other jobs to update
405
483
monitored_proxies .append (objectKey )
406
484
485
+ # Refresh the dependent proxies for the next update
486
+ for job , dependents in iteritems (self .__dependentJobs ):
487
+ ids = [d .id () for d in dependents ]
488
+ # If the job has no dependents, then ids is an empty list,
489
+ # The getJobs call returns every job on the cue when called
490
+ # an empty list for the id argument!
491
+ if not ids :
492
+ continue
493
+ tmp = opencue .api .getJobs (id = ids , all = True )
494
+ self .__dependentJobs [job ] = tmp
495
+
407
496
if self .__loadMine :
408
497
# This auto-loads all the users jobs
409
498
for job in opencue .api .getJobs (user = [cuegui .Utils .getUsername ()]):
410
- objectKey = cuegui .Utils .getObjectKey (job )
411
- jobs [objectKey ] = job
499
+ self .addJob (job )
412
500
413
501
# Prune the users jobs from the remaining proxies to update
414
502
for proxy , job in list (jobs .items ()):
@@ -438,30 +526,46 @@ def _processUpdate(self, work, rpcObjects):
438
526
for proxy , item in list (self ._items .items ()):
439
527
if not proxy in rpcObjects :
440
528
rpcObjects [proxy ] = item .rpcObject
441
-
529
+ # pylint: disable=too-many-nested-blocks
442
530
try :
443
531
selectedKeys = [
444
532
cuegui .Utils .getObjectKey (item .rpcObject ) for item in self .selectedItems ()]
445
533
scrolled = self .verticalScrollBar ().value ()
534
+ expanded = [cuegui .Utils .getObjectKey (item .rpcObject )
535
+ for item in self ._items .values () if item .isExpanded ()]
446
536
447
537
# Store the creation time for the current item
448
538
for item in list (self ._items .values ()):
449
539
self .__jobTimeLoaded [cuegui .Utils .getObjectKey (item .rpcObject )] = item .created
540
+ # Store the creation time for the dependent jobs
541
+ for item in self ._dependent_items .values ():
542
+ self .__jobTimeLoaded [cuegui .Utils .getObjectKey (item .rpcObject )] = item .created
450
543
451
544
self ._items = {}
452
545
self .clear ()
453
546
454
- for proxy , job in list (rpcObjects . items () ):
547
+ for proxy , job in iteritems (rpcObjects ):
455
548
self ._items [proxy ] = JobWidgetItem (job ,
456
549
self .invisibleRootItem (),
457
550
self .__jobTimeLoaded .get (proxy , None ))
458
551
if proxy in self .__userColors :
459
552
self ._items [proxy ].setUserColor (self .__userColors [proxy ])
553
+ if self .__groupDependent :
554
+ dependent_jobs = self .__dependentJobs .get (proxy , [])
555
+ for djob in dependent_jobs :
556
+ item = JobWidgetItem (djob ,
557
+ self ._items [proxy ],
558
+ self .__jobTimeLoaded .get (proxy , None ))
559
+ dkey = cuegui .Utils .getObjectKey (djob )
560
+ self ._dependent_items [dkey ] = item
561
+ if dkey in self .__userColors :
562
+ self ._dependent_items [dkey ].setUserColor (
563
+ self .__userColors [dkey ])
460
564
461
565
self .verticalScrollBar ().setRange (scrolled , len (rpcObjects .keys ()) - scrolled )
462
566
list (map (lambda key : self ._items [key ].setSelected (True ),
463
567
[key for key in selectedKeys if key in self ._items ]))
464
-
568
+ list ( self . _items [ key ]. setExpanded ( True ) for key in expanded if key in self . _items )
465
569
except opencue .exception .CueException as e :
466
570
list (map (logger .warning , cuegui .Utils .exceptionOutput (e )))
467
571
finally :
0 commit comments