Source code for cmasher.app_usage

"""
Application usage
=================
Holds function definitions useful for porting *CMasher* colormaps to other
applications.

"""

# %% IMPORTS
# Built-in imports
import re
from os import path
from textwrap import dedent, indent

# Import packages
import cmasher as cmr

# All declaration
__all__ = ["update_tableau_pref_file"]


# %% FUNCTION DEFINITIONS
# Define function that generates a Tableau properties file with colormaps
[docs] def update_tableau_pref_file(dirname: str = ".") -> None: """ Update an existing Tableau 'Preferences.tps' file to include colormaps from *CMasher*. The file is created if it does not exist yet. Optional -------- dirname : str. Default: '.' The relative or absolute path to the directory where the Tableau preferences file should be updated. If `dirname` contains an existing file called 'Preferences.tps', it will be updated to include *CMasher* colormap data. Otherwise, this file will be created in `dirname`. Note ---- In *CMasher*, colormaps sometimes get modified or renamed. This function takes these cases into account as well. """ # Obtain all colormaps in CMasher without reversed versions # This is because Tableau already has a function for this cmaps = [y for y in cmr.cm.cmap_d.values() if not y.name.endswith("_r")] # Create a dict that contains the Tableau type for each colormap type cmap_types = { "sequential": "ordered-sequential", "diverging": "ordered-diverging", "cyclic": "regular", } # Create empty dict of color-palette entries for all colormaps entries_dict: dict[str, str] = {} # Loop over all colormaps and create their color-palette entries for cmap in cmaps: # Obtain the type of this colormap cmap_type = cmap_types[cmr.get_cmap_type(cmap)] # Obtain all colors of this colormap in HEX-format colors_hex = cmr.take_cmap_colors(cmap, N=None, return_fmt="hex") # Create a list with all color representations in HEX colors_list = [f"<color>{x}</color>" for x in colors_hex] # Combine all these colors into a single string colors_str = "\n".join(colors_list) # Make sure to indent all lines in this string by 1 tab colors_str = indent(colors_str, "\t").expandtabs(4) # Create color-palette entry string entry_str = dedent( """ <color-palette name="cmr.{0}" type="{1}"> {2} </color-palette>""" ).format(cmap.name, cmap_type, colors_str)[1:] # Indent this string by 1 tab entry_str = indent(entry_str, "\t").expandtabs(4) # Add this entry to the dict entries_dict[cmap.name] = entry_str # Obtain absolute path to preferences file in provided dirname filename = path.abspath(path.join(dirname, "Preferences.tps")) # Check if this file already exists if path.exists(filename): # If so, read in the file contents with open(filename) as f: text = f.read() # Define the strings that enclose the colormap entries usually start_str = "<workbook>\n <preferences>\n" end_str = "\n </preferences>\n</workbook>" # Search for these strings start_idx = text.find(start_str) + 29 end_idx = text.find(end_str) sub_contents = text[start_idx:end_idx] # Now search this sub_contents string for all colormap names cmap_names = re.findall(r"\"cmr\.(\w+)\"", sub_contents) # Search entries_dict for all cmap_names for name in cmap_names: if name in entries_dict: continue # If not, obtain the entire entry idx = sub_contents.find("cmr." + name) start_idx_entry = idx - 25 if ( match := re.search( r"<\/color-palette>\n", sub_contents[start_idx_entry:] ) ) is None: raise RuntimeError end_idx_entry = match.end() + start_idx_entry # Remove this entry from sub_contents sub_contents = "".join( [sub_contents[:start_idx_entry], sub_contents[end_idx_entry:]] ) # Search this sub_contents string for all strings in entries_dict for name, cmap_entry in dict(entries_dict).items(): # Check if this colormap name already exists idx = sub_contents.find("cmr." + name) if idx == -1: continue # obtain the entire entry start_idx_entry = idx - 25 if ( match := re.search(r"<\/color-palette>", sub_contents[start_idx_entry:]) ) is None: raise RuntimeError end_idx_entry = match.end() + start_idx_entry # Replace this entry with the new entry sub_contents = "".join( [ sub_contents[:start_idx_entry], cmap_entry, sub_contents[end_idx_entry:], ] ) # Remove cmap from entries_dict entries_dict.pop(name) # Combine everything remaining in entries_dict together entries_str = "\n".join(["", *entries_dict.values()]) # Join sub_contents and entries_str together sub_contents = "".join([sub_contents, entries_str]) # Insert the sub_contents into pref_file_contents text = "".join([text[:start_idx], sub_contents, text[end_idx:]]) # Save this to the preferences file with open(filename, "w") as f: f.write(text) else: # If not, combine everything in entries_dict together to single string entries_str = "\n".join(entries_dict.values()) # Create the string for the new 'Preferences.tps' file pref_file = dedent( """ <?xml version='1.0'?> <workbook> <preferences> {0} </preferences> </workbook>""" ).format(entries_str)[1:] # Create this file with open(filename, "w") as f: f.write(pref_file)