Source code for pyworkflow.gui.project.utils

# **************************************************************************
# *
# * Authors:     Pablo Conesa (pconesa@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 3 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************
import datetime
import platform
import abc
from abc import ABC

from pyworkflow.gui.project.constants import STATUS_COLORS, WARNING_COLOR
from pyworkflow.protocol import STATUS_FAILED
from pyworkflow.viewer import ProtocolViewer


[docs]def getStatusColorFromNode(node): # If it is a run node (not PROJECT) return getStatusColorFromRun(node.run)
[docs]def getStatusColorFromRun(prot): """ Returns the color associated with the status. """ if prot: if prot.hasSummaryWarnings(): return WARNING_COLOR else: return getStatusColor(prot.status.get(STATUS_FAILED)) else: return getStatusColor()
[docs]def getStatusColor(status=None, default='#ADD8E6'): """ Parameters ---------- status status of the protocol Returns the color associated with he status ------- """ return STATUS_COLORS[status] if status else default
# OS dependent behaviour. Add any OS dependent method here and later we might move # or refactor this to a class or something else
[docs]class OSHandler(abc.ABC): """ Abstract class: Handler for OS specific actions"""
[docs] def maximizeWindow(root): pass
[docs]class LinuxHandler(OSHandler, ABC):
[docs] def maximizeWindow(root): root.attributes("-zoomed", True)
[docs]class MacHandler(OSHandler, ABC):
[docs] def maximizeWindow(root): root.state("zoomed")
[docs]class WindowsHandler(OSHandler, ABC):
[docs] def maximizeWindow(root): root.state("zoomed")
[docs]class OS: _handler = None _handlers = {"Linux": LinuxHandler, "Darwin": MacHandler, "Windows": WindowsHandler} # Until testing this on windows
[docs] @staticmethod def getPlatform(): return platform.system()
[docs] @classmethod def handler(cls): if cls._handler is None: cls._handler = cls._handlers[cls.getPlatform()] return cls._handler
[docs]def isAFinalProtocol(v, k): if (issubclass(v, ProtocolViewer) or v.isBase() or v.isDisabled()): return False return v.__name__ == k
[docs]def inspectObj(object, filename, prefix='', maxDeep=5, inspectDetail=2, memoryDict=None): """ Creates a .CSV file in the filename path with all its members and recursively with a certain maxDeep, if maxDeep=0 means no maxDeep (until all members are inspected). inspectDetail can be: - 1: All attributes are shown - 2: All attributes are shown and iterable values are also inspected prefix and memoryDict will be updated in the recursive entries: - prefix is a compound of the two first columns (DEEP and Tree) - memoryDict is a dictionary with the memory address and an identifier """ END_LINE = '\n' # end of line char COL_DELIM = '\t' # column delimiter INDENT_COUNTER = '/' # character append in each indention (it's not written) NEW_CHILD = ' |------> ' # new item indention BAR_CHILD = ' | ' + INDENT_COUNTER # bar indention END_CHILD = (' -- ' + COL_DELIM) * 4 + END_LINE # Child ending column1 = ' - Name - ' + COL_DELIM column2 = ' - Type - ' + COL_DELIM column3 = ' - Value - ' + COL_DELIM column4 = ' - Memory Address -' # Constants to distinguish the first, last and middle rows IS_FIRST = 1 IS_LAST = -1 IS_MIDDLE = 0 memoryDict = memoryDict or {} def writeRow(name, value, prefix, posList=False): """ Writes a row item. """ # we will avoid to recursively print the items wrote before # (ie. with the same memory address), thus we store a dict with the # addresses and the flag isNew is properly set if str(hex(id(value))) in memoryDict: memorySTR = memoryDict[str(hex(id(value)))] isNew = False else: # if the item is new, we save its memory address in the memoryDict # and we pass the name and the line on the file as a reference. memorySTR = str(hex(id(value))) file = open(filename, 'r') lineNum = str(len(file.readlines()) + 1) file.close() nameDict = str(name)[0:15] + ' ...' if len(str(name)) > 25 else str(name) memoryDict[str(hex(id(value)))] = '>>> ' + nameDict + ' - L:' + lineNum isNew = True if posList: # if we have a List, the third column is 'pos/lenght' thirdCol = posList else: # else, we print the value avoiding the EndOfLine char (// instead) thirdCol = str(value).replace(END_LINE, ' // ') # we will print the indentation deep number in the first row indentionDeep = prefix.count(INDENT_COUNTER) deepStr = str(indentionDeep) + COL_DELIM # the prefix without the indentCounters is # the tree to be printed in the 2nd row prefixToWrite = prefix.replace(INDENT_COUNTER, '') file = open(filename, 'a') file.write(deepStr + prefixToWrite + COL_DELIM + str(name) + COL_DELIM + str(type(value)) + COL_DELIM + thirdCol + COL_DELIM + memorySTR + END_LINE) file.close() return isNew def recursivePrint(value, prefix, isFirstOrLast): """ We print the childs items of tuples, lists, dicts and classes. """ # if it's the last item, its childs has not the bar indention if isFirstOrLast == IS_LAST: # void indention when no more items prefixList = prefix.split(INDENT_COUNTER) prefixList[-2] = prefixList[-2].replace('|', ' ') prefix = INDENT_COUNTER.join(prefixList) # recursive step with the new prefix and memory dict. inspectObj(value, filename, prefix + BAR_CHILD, maxDeep, inspectDetail, memoryDict) if isFirstOrLast == IS_FIRST: deepStr = str(indentionDeep) + COL_DELIM else: # When it was not the first item, the deep is increased # to improve the readability when filter deepStr = str(indentionDeep + 1) + COL_DELIM prefix = prefix.replace(INDENT_COUNTER, '') + COL_DELIM # We introduce the end of the child and # also the next header while it is not the last file = open(filename, 'a') file.write(deepStr + prefix + END_CHILD) if isFirstOrLast != IS_LAST: # header file.write(deepStr + prefix + column1 + column2 + column3 + column4 + END_LINE) file.close() def isIterable(obj): """ Returns true if obj is a tuple, list, dict or calls. """ isTupleListDict = (isinstance(obj, tuple) or isinstance(obj, dict) or isinstance(obj, list)) and len(value) > 1 # FIX ME: I don't know how to assert if is a class or not... isClass = str(type(obj))[1] == 'c' return isClass or (isTupleListDict and inspectDetail < 2) indentionDeep = prefix.count(INDENT_COUNTER) if indentionDeep == 0: prefix = ' - Root - ' # dict with name and value pairs of the members if len(object) == 1: # if only one obj is passed in the input list, # we directly inspect that obj. obj_dict = object[0].__dict__ object = object[0] # setting the header row treeHeader = ' - Print on ' + str(datetime.datetime.now()) prefixHeader = '-DEEP-' + COL_DELIM + treeHeader + COL_DELIM col1 = ' - Name - (value for Lists and Tuples)' + COL_DELIM col3 = ' - Value - (Pos./Len for Lists and Tuples) ' + COL_DELIM # writing the header row file = open(filename, 'w') file.write(prefixHeader + col1 + column2 + col3 + column4 + END_LINE) file.close() # writing the root object writeRow(object.__class__.__name__, object, prefix) # adding the child bar to the prefix prefix = ' ' + BAR_CHILD else: # firsts settings depending on the type of the obj if str(type(object))[1] == 'c': obj_dict = object.__dict__ elif (isinstance(object, tuple) or isinstance(object, list)): column1 = ' - Value - ' + COL_DELIM column3 = ' - Pos./Len. - ' + COL_DELIM elif isinstance(object, dict): column1 = ' - Key - ' + COL_DELIM obj_dict = object else: # if is not of the type above it not make sense to continue return indentionDeep = prefix.count(INDENT_COUNTER) deepStr = str(indentionDeep) + COL_DELIM isBelowMaxDeep = indentionDeep < maxDeep if maxDeep > 0 else True prefixToWrite = prefix.replace(INDENT_COUNTER, '') + COL_DELIM file = open(filename, 'a') file.write(deepStr + prefixToWrite + column1 + column2 + column3 + column4 + END_LINE) file.close() # we update the prefix to put the NEW_CHILD string ( |----> ) prefixList = prefix.split(INDENT_COUNTER) prefixList[-2] = NEW_CHILD # we return to the string structure # with a certain indention if it's the root prefixToWrite = ' ' + INDENT_COUNTER.join(prefixList) if indentionDeep == 1 \ else INDENT_COUNTER.join(prefixList) isNew = True if str(type(object))[1] == 'c' or isinstance(object, dict): counter = 0 for key, value in obj_dict.items(): counter += 1 # write the variable isNew = writeRow(key, value, prefixToWrite) # managing the extremes of the loop if counter == 1: isFirstOrLast = IS_FIRST elif counter == len(obj_dict): isFirstOrLast = IS_LAST else: isFirstOrLast = IS_MIDDLE # show attributes for objects and items for lists and tuples if isBelowMaxDeep and isNew and isIterable(value): recursivePrint(value, prefix, isFirstOrLast) else: for i in range(0, len(object)): # write the variable isNew = writeRow(object[i], object[i], prefixToWrite, str(i + 1) + '/' + str(len(object))) # managing the extremes of the loop if i == 0: isFirstOrLast = IS_FIRST elif len(object) == i + 1: isFirstOrLast = IS_LAST else: isFirstOrLast = IS_MIDDLE # show attributes for objects and items for lists and tuples if isBelowMaxDeep and isNew and isIterable(object[i]): recursivePrint(object[i], prefix, isFirstOrLast)