Skip to content

Commit

Permalink
Merge pull request #21 from 1313e/cli_tools
Browse files Browse the repository at this point in the history
Release of v1.5.0: CLI Tools
  • Loading branch information
1313e authored Aug 11, 2020
2 parents a9803b3 + bdecf71 commit 942cd9c
Show file tree
Hide file tree
Showing 52 changed files with 893 additions and 420 deletions.
8 changes: 0 additions & 8 deletions .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
environment:
matrix:
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "32"

- PYTHON: "C:\\Python27-x64"
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "64"

- PYTHON: "C:\\Python35"
PYTHON_VERSION: "3.5.x"
PYTHON_ARCH: "32"
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ env:

jobs:
include:
- python: 2.7
- python: 3.5
- python: 3.6
- python: 3.7
Expand Down
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

*CMasher*: Scientific colormaps for making accessible, informative and *cmashing* plots
=======================================================================================
The *CMasher* package provides a collection of scientific colormaps to be used by different *Python* packages and projects, mainly in combination with `matplotlib`_, showcased in the `online documentation`_.
The *CMasher* package provides a collection of scientific colormaps and utility functions to be used by different *Python* packages and projects, mainly in combination with `matplotlib`_, showcased in the `online documentation`_.
The colormaps in *CMasher* are all designed to be perceptually uniform sequential using the `viscm`_ package; most of them are color-vision deficiency friendly; and they cover a wide range of different color combinations to accommodate for most applications.
It offers several alternatives to commonly used colormaps, like *chroma* and *rainforest* for *jet*; *sunburst* for *hot*; *neutral* for *binary*; and *fusion* and *redshift* for *coolwarm*.
If you cannot find your ideal colormap, then please open an `issue`_, provide the colors and/or style you want, and I will try to create one to your liking!
Expand Down Expand Up @@ -75,13 +75,13 @@ So, for example, if one were to use the *rainforest* colormap, this could be don
plt.scatter(x, y, c=z, cmap=cmap, s=300)
plt.show()
For other use-cases, see the `online documentation`_.
For other use-cases, including an overview of *CMasher*'s utility functions, see the `online documentation`_.


.. |PyPI| image:: https://img.shields.io/pypi/v/CMasher.svg?logo=pypi&logoColor=white&label=PyPI
:target: https://pypi.python.org/pypi/CMasher
:alt: PyPI - Latest Release
.. |Python| image:: https://img.shields.io/badge/Python-2.7%20%7C%203.5%2B-blue?logo=python&logoColor=white
.. |Python| image:: https://img.shields.io/badge/Python-3.5%2B-blue?logo=python&logoColor=white
:target: https://pypi.python.org/pypi/CMasher
:alt: PyPI - Python Versions
.. |Travis| image:: https://img.shields.io/travis/com/1313e/CMasher/master.svg?logo=travis%20ci&logoColor=white&label=Travis%20CI
Expand Down
3 changes: 0 additions & 3 deletions cmasher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@


# %% IMPORTS AND DECLARATIONS
# Future imports
from __future__ import absolute_import, division, print_function

# CMasher imports
from .__version__ import __version__
from . import utils
Expand Down
2 changes: 1 addition & 1 deletion cmasher/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

# %% VERSIONS
# Default/Latest/Current version
__version__ = '1.4.3'
__version__ = '1.5.0'
315 changes: 315 additions & 0 deletions cmasher/cli_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
# -*- coding: utf-8 -*-

# %% IMPORTS
# Built-in imports
import argparse
from importlib import import_module
import os
import sys

# Package imports
import e13tools as e13
import numpy as np

# CMasher imports
from cmasher import __version__
import cmasher as cmr

# All declaration
__all__ = ['main']


# %% GLOBALS
# Define main description of this package
main_desc = ("CMasher: Scientific colormaps for making accessible, informative"
" and 'cmashing' plots")


# %% CLASS DEFINITIONS
# Define formatter that automatically extracts help strings of subcommands
class HelpFormatterWithSubCommands(argparse.ArgumentDefaultsHelpFormatter):
# Override the add_argument function
def add_argument(self, action):
# Check if the help of this action is required
if action.help is not argparse.SUPPRESS:
# Check if this action is a subparser's action
if isinstance(action, argparse._SubParsersAction):
# If so, sort action.choices on name
names = sorted(action.choices.keys())

# Loop over all subcommands defined in the action
for name in names:
# Obtain corresponding subparser
subparser = action.choices[name]

# Format the description of this subcommand and add it
self._add_item(self.format_subcommands,
[name, subparser.description])
# Call super method in all other cases
else:
super().add_argument(action)

# This function formats the description of a subcommand with given name
def format_subcommands(self, name, description):
# Determine the positions and widths of the help texts
help_position = min(self._action_max_length+2, self._max_help_position)
help_width = max(self._width-help_position, 11)
name_width = help_position-self._current_indent-2

# Transform name to the proper formatting
name = "{0}{1: <{2}}{3}".format(
' '*self._current_indent, name, name_width,
' ' if(len(name) <= name_width) else '\n'+' '*help_position)

# Split the lines of the subcommand description
desc_lines = self._split_lines(description, help_width)

# Create list of all parts of the description of this subcommand
parts = [name, desc_lines.pop(0), '\n']

# Loop over all remaining desc_lines
for line in desc_lines:
# Format and add to parts
parts.append("%s%s\n" % (' '*help_position, line))

# Convert to a single string and return
return(''.join(parts))


# %% COMMAND FUNCTION DEFINITIONS
# This function handles the 'bibtex' subcommand
def cli_bibtex():
cmr.get_bibtex()


# This function handles the 'cmap_type' subcommand
def cli_cmap_type():
# Import cmap packages
import_cmap_pkgs()

# Print cmap type
print(cmr.get_cmap_type(ARGS.cmap))


# This function handles the 'take_cmap_colors' subcommand
def cli_cmap_colors():
# Import cmap packages
import_cmap_pkgs()

# Obtain the colors
colors = cmr.take_cmap_colors(ARGS.cmap, ARGS.ncolors,
cmap_range=ARGS.cmap_range,
return_fmt=ARGS.return_fmt)

# Print the colors line-by-line
if ARGS.return_fmt in ('float', 'norm'):
np.savetxt(sys.stdout, colors, '%.8f')
elif ARGS.return_fmt in ('int', '8bit'):
np.savetxt(sys.stdout, colors, '%i')
else:
np.savetxt(sys.stdout, colors, '%s')


# This function handles the 'mkcmod' subcommand
def cli_mk_cmod():
# Create cmap module
cmap_path = cmr.create_cmap_mod(ARGS.cmap, save_dir=ARGS.dir)

# Print on commandline that module has been created
print("Created standalone colormap module of %r in %r."
% (ARGS.cmap, cmap_path))


# %% FUNCTION DEFINITIONS
# This function attempts to import a collection of packages with colormaps
def import_cmap_pkgs():
# Define set of packages with colormaps
cmap_pkgs = {'cmocean', 'colorcet', 'palettable'}

# Obtain packages from CMR_CMAP_PKGS environment variable
env_pkgs = os.environ.get('CMR_CMAP_PKGS', None)

# Add env_pkgs to cmap_pkgs if it is not empty
if env_pkgs is not None:
# If Windows, split variable at semicolons
if sys.platform.startswith('win'):
env_pkgs = env_pkgs.split(';')
# Else, if UNIX, split variable at colons
elif sys.platform.startswith(('darwin', 'linux')):
env_pkgs = env_pkgs.split(':')
# Else, ignore the variable
else:
env_pkgs = []

# Add pkgs
cmap_pkgs.update(env_pkgs)

# Attempt to import each package
for cmap_pkg in cmap_pkgs:
try:
import_module(cmap_pkg)
except ImportError:
pass


# %% MAIN FUNCTION
def main():
"""
This is the main function of the CLI and is called whenever `cmr` is
invoked from the command-line.
"""

# Initialize argparser
parser = argparse.ArgumentParser(
'cmr',
description=main_desc,
formatter_class=HelpFormatterWithSubCommands,
add_help=True,
allow_abbrev=True)

# Add subparsers
subparsers = parser.add_subparsers(
title='commands',
metavar='COMMAND')

# OPTIONAL ARGUMENTS
# Add 'version' argument
parser.add_argument(
'-v', '--version',
action='version',
version="CMasher v{}".format(__version__))

# Create a cmap parser for several commands
cmap_parent_parser = argparse.ArgumentParser(add_help=False)

# Add 'cmap' argument
cmap_parent_parser.add_argument(
'cmap',
help="Name of colormap to use, as registered in *matplotlib*",
metavar='CMAP',
action='store',
type=str)

# BIBTEX COMMAND
# Add bibtex subparser
bibtex_parser = subparsers.add_parser(
'bibtex',
description=e13.get_main_desc(cmr.get_bibtex),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=True)

# Set defaults for bibtex_parser
bibtex_parser.set_defaults(func=cli_bibtex)

# CMAP_TYPE COMMAND
# Add cmap_type subparser
cmap_type_parser = subparsers.add_parser(
'cmtype',
parents=[cmap_parent_parser],
description=e13.get_main_desc(cmr.get_cmap_type),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=True)

# Set defaults for cmap_type_parser
cmap_type_parser.set_defaults(func=cli_cmap_type)

# CMAP_COLORS COMMAND
# Obtain the optional default arguments of take_cmap_colors
defaults = cmr.take_cmap_colors.__kwdefaults__

# Create a return_fmt parser
return_fmt_parent_parser = argparse.ArgumentParser(add_help=False)

# Add 'fmt' optional argument
return_fmt_parent_parser.add_argument(
'--fmt',
help="Format to return colors in",
action='store',
default=defaults['return_fmt'],
choices=['float', 'norm', 'int', '8bit', 'str', 'hex'],
type=str,
dest='return_fmt')

# Add cmap_colors subparser
cmap_colors_parser = subparsers.add_parser(
'cmcolors',
parents=[cmap_parent_parser, return_fmt_parent_parser],
description=e13.get_main_desc(cmr.take_cmap_colors),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=True)

# Add 'N' argument
cmap_colors_parser.add_argument(
'ncolors',
help="Number of colors to take",
metavar='N',
action='store',
type=int)

# Add 'cmap_range' optional argument
cmap_colors_parser.add_argument(
'--range',
help=("Normalized value range in the colormap from which colors should"
" be taken"),
metavar=('LOWER', 'UPPER'),
action='store',
nargs=2,
default=defaults['cmap_range'],
type=float,
dest='cmap_range')

# Set defaults for cmap_colors_parser
cmap_colors_parser.set_defaults(func=cli_cmap_colors)

# RGB_TABLE COMMAND
# Add rgb_table subparser
rgb_table_parser = subparsers.add_parser(
'rgbtable',
parents=[cmap_parent_parser, return_fmt_parent_parser],
description="Retrieves the RGB values of the provided `cmap`.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=True)

# Set defaults for rgb_table_parser
rgb_table_parser.set_defaults(func=cli_cmap_colors,
ncolors=None,
cmap_range=defaults['cmap_range'])

# MK_CMOD COMMAND
# Add mk_cmod subparser
mk_cmod_parser = subparsers.add_parser(
'mkcmod',
description=e13.get_main_desc(cmr.create_cmap_mod),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=True)

# Add 'cmap' argument
mk_cmod_parser.add_argument(
'cmap',
help="Name of *CMasher* colormap to create standalone module for",
metavar='CMAP',
action='store',
type=str)

# Add 'dir' optional argument
mk_cmod_parser.add_argument(
'-d', '--dir',
help="Path to directory where the module must be saved",
action='store',
default=cmr.create_cmap_mod.__kwdefaults__['save_dir'],
type=str)

# Set defaults for mk_cmod_parser
mk_cmod_parser.set_defaults(func=cli_mk_cmod)

# Parse the arguments
global ARGS
ARGS = parser.parse_args()

# If arguments is empty (no func was provided), show help
if 'func' not in ARGS:
parser.print_help()
# Else, call the corresponding function
else:
ARGS.func()
6 changes: 3 additions & 3 deletions cmasher/colormaps/amber/amber.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@


# %% GLOBALS AND DEFINITIONS
# Type of this colormap (according to viscm)
cm_type = "linear"
# Type of this colormap
cm_type = 'sequential'

# RGB-values of this colormap
cm_data = [[0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
Expand Down Expand Up @@ -276,7 +276,7 @@
[9.83854395e-01, 9.54323974e-01, 3.40656523e-01]]

# Create ListedColormap object for this colormap
cmap = ListedColormap(cm_data, name="cmr.amber", N=len(cm_data))
cmap = ListedColormap(cm_data, name='cmr.amber', N=len(cm_data))
cmap_r = cmap.reversed()

# Register (reversed) cmap in MPL
Expand Down
Loading

0 comments on commit 942cd9c

Please sign in to comment.