Source code for mastoron.colors

import revitron
import mastoron
import colorsys
import os
import json
from revitron import _
from pyrevit import forms
from collections import defaultdict
from mastoron.variables import MASTORON_COLORSCHEME
from mastoron.variables import DATA, IS_INSTANCE, NAME, PARAM_TYPE


[docs]class Color: """ Class for basic color operations. """
[docs] def __init__(self): """ Inits a new Color instance. """ pass
[docs] @staticmethod def HSVtoRGB(hsv): """ Convert a color from hsv to rgb. Args: hsv (tuple): A color in hsv format. Returns: tuple: A color in rgb format """ return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2]))
[docs] @staticmethod def RGBtoHEX(rgb): return '%02x%02x%02x' % rgb
[docs] @staticmethod def HEXtoRGB(hex): """ Converts a hex color string to rgb. Args: hex (sting): The hex color Returns: tuple: The rgb color """ hex = hex.lstrip('#') return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4))
[docs]class ColorScheme: """ Class for handling relationships between labels and colors. """ JSON_PATH = 'C:\\temp\\mastoron\\colorscheme.json'
[docs] def __init__(self): """ Inits a new ColorScheme instance. """ self.COLOR_SCHEMES = 'mastoron.colorschemes' self.schemes = mastoron.ConfigStorage().get( self.COLOR_SCHEMES, defaultdict()) self.defaultColors = [ '#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B' ] self.additionalColors = [ '#D32F2F', '#C2185B', '#7B1FA2', '#512DA8', '#303F9F', '#1976D2', '#0288D1', '#0097A7', '#00796B', '#388E3C', '#689F38', '#AFB42B', '#FBC02D', '#FFA000', '#F57C00', '#E64A19', '#5D4037', '#616161', '#455A64' ] self.extendedColors = self.defaultColors + self.additionalColors
[docs] @staticmethod def toJSON(data, path=JSON_PATH): """ Write color scheme to json Args: data (dict): The data to export path (str, optional): The output file path. Defaults to 'C:\temp\mastoron\colorscheme.json'. Returns: string: The json file """ directory = os.path.dirname(path) if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: json.dump(data, f) return path
[docs] @staticmethod def fromJSON(path): """ Reads a color scheme from json. Args: path (string): The json file Returns: dict: The color scheme """ with open(path, 'r') as f: scheme = json.load(f) return scheme
[docs] @staticmethod def getFromUser(excludeSchemes=None, excludeViews=None): """ Asks the user to select a mastoron color scheme. Args: excludeSchemes (string, optional): The name of one or more schemes to exclude. Defaults to None. excludeViews (string, optional): The id(s) of one ore more views to exclude. Defaults to None. Returns: dict: The selected mastoron color scheme """ if not type(excludeSchemes) == list: excludeSchemes = [excludeSchemes] if excludeViews: if not type(excludeViews) == list: excludeViews = [excludeViews] schemes = [] for scheme in ColorScheme().schemes: if not scheme[NAME] in excludeSchemes: schemes.append(scheme) names = [scheme[NAME] for scheme in schemes] if excludeViews: for scheme in schemes: for viewId in excludeViews: try: if not str(viewId) in mastoron.AffectedViews().get(scheme): names.remove(scheme[NAME]) except: names.remove(scheme[NAME]) schemeName = forms.CommandSwitchWindow.show(sorted(names), message='Choose Color Scheme:') if not schemeName: return None return ColorScheme().load(schemeName)
[docs] @staticmethod def apply(view, elements, schemeName, isInstance, type, patternId): """ Applies a mastoron color scheme to given elements in given view. Updates the colors scheme with new keys and colors. Args: view (object): A Revit view elements (object): A list of Revit elements schemeName (string): The name of the color scheme isInstance (bool): True for instance parameters, false for type parameters type (string): The type of the parameter (Area, Number, Length, etc..) patternId (object): The Revit element id of the fillpattern to use Returns: dict: The applied and updated color scheme """ keys = set() for element in elements: key = mastoron.GetKey(element, schemeName, isInstance, type) if key: keys.add(key) scheme = ColorScheme().load(schemeName) if not scheme: scheme = ColorScheme().generate(schemeName, keys, isInstance) if not scheme: return None elif scheme: ColorScheme().update(scheme, keys) ColorScheme().save(scheme) overriddenElements = mastoron.AffectedElements().get( scheme, viewId=view.Id) for element in elements: key = mastoron.GetKey(element, schemeName, isInstance, type) if key: colorHEX = scheme[DATA][key] colorRGB = mastoron.Color.HEXtoRGB(colorHEX) mastoron.ElementOverrides(view, element).set(colorRGB, patternId) overriddenElements.append(str(element.Id)) else: mastoron.ElementOverrides(view, element).clear() try: overriddenElements.remove(str(element.Id)) except: pass mastoron.AffectedElements().dump(scheme, view.Id, overriddenElements) return scheme
[docs] def generate(self, schemeName, keys, isInstance=None, paramType=None, excludeColors=None, gradient=False): """ Generates a new color scheme. Args: schemeName (string): The name of the color scheme keys (string): A set of keys excludeColors (string, optional): A list of colors to exclude gradient (int, optional): A tuple with start and end color Returns: dict: A color scheme: {name: schemeName, data: {key: color}} """ if not gradient: colors = ColorScheme().getColors(len(keys), excludeColors) elif gradient: colorsHSV = ColorRange(len(keys), min=gradient[0], max=gradient[1]).getHSV() colors = [] for hsv in colorsHSV: rgb = Color.HSVtoRGB(hsv) colors.append(Color.RGBtoHEX(rgb)) scheme = {} scheme[NAME] = schemeName scheme[IS_INSTANCE] = isInstance scheme[PARAM_TYPE] = paramType scheme[DATA] = {} for value, color in zip(sorted(keys), colors): scheme[DATA][value] = color return scheme
[docs] def update(self, colorScheme, keys): """ Updates a given color scheme with new keys and default colors. Args: colorScheme (dict): The color scheme to update keys (set): The keys to add Returns: dict: The updated color scheme """ newkeys = set() for key in keys: if key not in colorScheme[DATA].keys(): newkeys.add(key) if newkeys: excludeColors = colorScheme[DATA].values() tempScheme = ColorScheme().generate( 'tempName', newkeys, excludeColors=excludeColors) if not tempScheme: return None colorScheme[DATA].update(tempScheme[DATA]) return colorScheme
[docs] def load(self, schemeName): """ Loads a color scheme by name. Args: schemeName (string): The name of the color scheme Returns: dict: The color scheme """ for scheme in self.schemes: if scheme[NAME] == schemeName: return scheme return None
[docs] def save(self, scheme): """ Saves a color scheme to the revitron DocumentConfigStorage. Args: scheme (dict): A color scheme """ writeSchemes = [] update = False for existingScheme in self.schemes: if scheme[NAME] == existingScheme[NAME]: existingScheme[DATA] = scheme[DATA] update = True writeSchemes.append(existingScheme) if not update: writeSchemes.append(scheme) mastoron.ConfigStorage().set(self.COLOR_SCHEMES, writeSchemes)
[docs] def delete(self, scheme): """ Deletes a color scheme from the revitron DocumenConfigStorage. Args: scheme (dict): A color scheme """ usedSchemes = [] for docScheme in self.schemes: if not docScheme[NAME] == scheme[NAME]: usedSchemes.append(docScheme) mastoron.ConfigStorage().set(self.COLOR_SCHEMES, usedSchemes)
[docs] def getColors(self, count, excludeColors=[]): """ Gets a given amount of colors. Args: count (int): The number of colors to get excludeColors (string, optional): List of colors to exclude. Defaults to None. Returns: string: A list of colors """ import random def filterColors(excludeColors, colors): if excludeColors: availableColors = filter( lambda color: color not in excludeColors, colors) return availableColors else: return colors self.defaultColors = filterColors(excludeColors, self.defaultColors) self.extendedColors = filterColors(excludeColors, self.extendedColors) if count <= len(self.defaultColors): availableColors = self.defaultColors elif count <= len(self.extendedColors): availableColors = self.extendedColors elif count >= len(self.extendedColors) and count < 100: hsvColors = ColorRange(count).getHSV() availableColors = [] for hsvColor in hsvColors: rgbColor = Color.HSVtoRGB(hsvColor) hexColor = Color.RGBtoHEX(rgbColor) availableColors.append(hexColor) else: print('Too many keys, colors are indistiguishable.') return None colors = random.sample(availableColors, count) return colors
[docs]class ColorRange: """ Class for working with color ranges. """
[docs] def __init__(self, count, min=0, max=100): """ Inits a new ColorRange instance. Accepted values:: 0 <= min < 100 1 < max <= 100 count < max - min """ if 0 <= min and min < 100: self.min = min else: return None if 1 < max and max <= 100: self.max = max else: return None if count < max - min: self.count = count else: return None self.range = max - min if not self.count <= self.range: print('Count bigger than range.') return None
[docs] def getHSV(self): """ Gets a list of colors in hsv format. Returns: list: A list of hsv colors """ hsv = [] for i in [ x * 0.01 for x in range( self.min, self.max, (self.range / self.count ) ) ]: hsv.append((i, 0.5, 0.9)) return hsv