Source code for pipeLion.ui.graphs.tableGraph

import logging
import datetime

from PyQt4 import QtGui, QtCore, Qt

from pipeLion.ui.graphs.tableBg import TableBg
from pipeLion.ui.graphs.tableTask import TableTask
from pipeLion.ui.utils.specialTypes import TableTuple
from pipeLion.ui.utils import colorManager

_log = logging.getLogger('pipeLion')


[docs]class TableGraph(QtGui.QGraphicsScene): """ TableGraph is derived by QGraphicsScene It defines a standardized way to show any kind of table rules for the tables can be defined by adding entries to the tables dictionary, which is a property-method of this class depending on which table-view is set by the currentTableType instance variable the scene paints different kind of tables and offers different editing modes. """ def getAllShots(self, item): from pipeLion.assets.shot import Shot project = item.getProject if not project: return [] if isinstance(item, Shot): columns = [ TableTuple(('Name',item.name())), TableTuple(('Image', QtGui.QIcon(item.image() or colorManager.noImage))), TableTuple(('First_Frame', item.first_frame())), TableTuple(('Last_Frame', item.last_frame())), TableTuple(('Description', item.description())) ] else: shot = item.getShot or item columns = [ TableTuple(('Name', shot.name())), ] user = None if item.user(): if isinstance(item.user(), list): user = [each.name() for each in item.user() if each] else: user = item.user().name() additionalColums = [ TableTuple((item.type(), item.name())), TableTuple(('state(%s)' % item.type()[:3].lower(), colorManager.getStateIcon(item.state()))), TableTuple(('user(%s)' % item.type()[:3].lower(), user)), ] columns.extend(additionalColums) return columns def getAllShotAttributes(self, selectedAssets): columns = ['Name', 'Image', 'First_Frame', 'Last_Frame', 'Description'] from pipeLion.assets.shot import Shot for eachAsset in selectedAssets: project = eachAsset.getProject if not project: return [] dependentTasks = [each for each in eachAsset.getDependencies(recursive=True)] dependentTasks.append(eachAsset) for eachDependency in dependentTasks: if eachDependency.type() not in columns: columns.extend([eachDependency.type(), 'state(%s)'%eachDependency.type()[:3].lower(), 'user(%s)'%eachDependency.type()[:3].lower()]) return columns def sortShotbreakdown(self, selectedAssets): from pipeLion.assets.shot import Shot listToBeSorted = \ sorted(selectedAssets, key=lambda item: \ ('%s_%s_%s_%s' % tuple([each[1] for each in self.getAllShots(item)])) \ if not isinstance(item, Shot) else \ '%s_%s' % (item.name(), item.first_frame()) ) return listToBeSorted
[docs] def getTimeline(self, selectedAssets): """ this method outsources a complex functionality from a lambda-function in the tables-property dictionary :param selectedAssets: list of the assets, represented by the Nodes, selected in the adminGraph :type selectedAssets: list of AbstractAsset :returns: a list of dates including the timespan of all assets in selectedAssets an in addition to a certain handleLength :rtype: list of datetime.timedelta """ allColumns = [] handles = datetime.timedelta(days=30) for i, eachAsset in enumerate(selectedAssets): if i: if eachAsset.end_date() >= last: last = eachAsset.end_date() if eachAsset.start_date() <= first: first = eachAsset.start_date() else: first = eachAsset.start_date() last = eachAsset.end_date() if selectedAssets: delta = ((last + datetime.timedelta(days=1)) - first) + (2 * handles) start = first - handles for i in xrange(delta.days): day = start + datetime.timedelta(days=i) newDate = datetime.date(day.year, day.month, day.day) allColumns.append(newDate) return allColumns
def getAllDays(self, asset): allValues = [] for i in xrange((asset.end_date()-asset.start_date()).days+1): date = asset.start_date()+datetime.timedelta(days=i) newDate = datetime.date(date.year, date.month, date.day) allValues.append(TableTuple((newDate, asset.name()))) return allValues
[docs] def moveUser(self, delta, asset): """ this method outsources a complex functionality from a lambda-function in the tables-property dictionary :param delta: amount of colums the mouse was moved in y position -1=one column to the left 1=one column top the right :type delta: int :param asset: asset on which the event was performed :type asset: AbstractAsset """ column = self.bg.getCurrentColumn(asset.user())+delta user = self.bg.getCurrentColumnInfo(column) if user and not isinstance(asset.user(), list): asset.assignUser(user) elif isinstance(asset.user(), list): _log.info('this task cannot be assigned, as it is a group-task') elif user == None: asset.deassignUser()
@property
[docs] def tables(self): """ tables returns a dictionary which holds lambda rules on how to treat a certain table-type the dictionary is build up as follows:: { 'X-Axsis/Y-Axis' : #string description eg: time/task { 'all' : lambda selectedAssets: function to get all values of the x-achsis, #mandatory 'getter' : lambda item: function to get all values of the current item, #mandatory 'sort' : lambda items: function to sort the output of 'all' and 'getter', #optional 'str' : lambda item: function to convert the item to a string(display information), #optional 'rowSorting' : lambda selectedAssets: allows to sort all rows from top to bottom #optional 'changeMin' : lambda delta, item: function to define what should be done with the item in case the in-point of an item was changed in the interface, #optional 'changeMax' : lambda delta, item: function to define what should be done with the item in case the out-point of an item was changed in the interface, #optional 'move' : lambda delta, item: function to define what should be done with the item in case an item was moved in the interface, #optional 'deleteExisting' : lambda column, item: function to define what should be done with the item in case the item was deleted in the interface, #optional 'createNew' : lambda column, item: function to define what should be done with the item in case a new item was created in the interface, #optional 'keepSeparate' : True/False indicates if the entries should be kept separately or if they should be grouped together. default is grouping #optional } } .. note:: all mandatory items have to be defined and all optional items define user-actions. if an action is not defined, the missing interaction is not available in the interface :returns: the table-definition-dictionary :rtype: dictionary """ return { 'time/task' : { 'all' : lambda selectedAssets: self.getTimeline(selectedAssets), 'getter' : lambda item : self.getAllDays(item), 'sort' : lambda items : sorted(items), 'rowSorting' : lambda selectedAssets: sorted(selectedAssets, key = lambda asset: asset.getShot.name() if asset.getShot else '', reverse=True ), 'move' : lambda delta, item : item.assetDict.update(\ {'start_date':item.start_date() + datetime.timedelta(days=delta), \ 'end_date':item.end_date() + datetime.timedelta(days=delta)}\ ), 'changeMin' : lambda delta, item : item.assetDict.update(\ {'start_date':item.start_date() + datetime.timedelta(days=delta)}\ ), 'changeMax' : lambda delta, item : item.assetDict.update(\ {'end_date':item.end_date() + datetime.timedelta(days=delta)}\ ), 'str' : lambda item : item.strftime('%y-%m-%d'), }, 'user/task' : { 'all' : lambda selectedAssets: selectedAssets[-1].getProject.user() if selectedAssets else [], 'getter' : lambda item : [TableTuple((each, item.name())) for each in item.user()] \ if isinstance(item.user(), list) \ else [TableTuple((item.user(), item.name()))] \ if item.user() \ else [], 'sort' : lambda items : items, 'move' : lambda delta, item : self.moveUser(delta, item), 'str': lambda item : item.name() if item else 'None', }, 'state/task' : { 'all' : lambda selectedAssets: [0, 1, 2], 'getter' : lambda item : [TableTuple((item.state(), item.name()))], 'sort' : lambda items : items, 'move' : lambda delta, item : item.set_state(item.state()+delta), }, 'task/shot' : { 'all' : lambda selectedAssets: self.getAllShotAttributes(selectedAssets), 'getter': lambda item : self.getAllShots(item), 'keepSeparate': True, 'rowSorting': lambda allAssets: self.sortShotbreakdown(allAssets), }, }
def __init__(self, selectedAssets=None, editable=True, parent=None): """ the constructor of TableGraph no argument needs to be set here, it can be changed anytime. :param selectedAssets: list of AbstractAsset-objects which should be used to create the table :type selectedAssets: list of AbstractAsset :param editable: indicates if the user should be able to change values in the table or not :type editable: bool :param parent: QGraphicsScene parent :type parent: QWidget """ if not selectedAssets: selectedAssets = [] super(TableGraph, self).__init__(parent) self.editable = editable self.currentTableType = 'time/task' self.selectedNodes = self.getSelectedNodes(selectedAssets) self.sortSelectedNodes() self.createAllTaskEntries() def sortSelectedNodes(self): if self.tables[self.currentTableType].has_key('rowSorting'): self.selectedNodes = self.tables[self.currentTableType]['rowSorting'](self.selectedNodes)
[docs] def getSelectedNodes(self, selectedAssets): """ method will generate a list with all dependent assets from all selected assets without double entries :param selectedAssets: list of selected assets :type selectedAssets: list of AbstractAsset :returns: list of assets :rtype: list of AbstractAsset """ allAssets = set(selectedAssets) for eachAsset in list(allAssets)[:]: dependentAssets = eachAsset.getDependencies(True) allAssets = allAssets | set(dependentAssets) allAssets = list(allAssets) return allAssets
[docs] def createAllTaskEntries(self): """ this will create all table-items and the table-background and add them to its scene. """ self.allItems = {} self.bg = TableBg(self.tables[self.currentTableType], self.selectedNodes) self.addItem(self.bg) for i, eachAsset in enumerate(self.selectedNodes): eachItem = TableTask(self.tables[self.currentTableType], eachAsset, i, self.bg, self.editable) self.allItems[eachAsset] = eachItem self.addItem(self.allItems[eachAsset])
[docs] def updateTable(self, selectedNodes): """ method will repaint the entire scene """ self.selectedNodes = self.getSelectedNodes(selectedNodes) self.sortSelectedNodes() self.clear() self.bg.updateTable(self.selectedNodes) self.createAllTaskEntries()
[docs] def updateItems(self): """ method to update all items in the table """ for eachAsset, eachItem in self.allItems.iteritems(): eachItem.updateData()
[docs] def tableTypeChanged(self, tableType): """ method to define what happens, when the table-type changes :param tableType: name of the new tableType :type tableType: string """ self.currentTableType = str(tableType) self.clear() for eachView in self.views(): eachView.centerOn(0.0,0.0) self.sortSelectedNodes() self.bg.updateTable(self.selectedNodes) self.createAllTaskEntries()