summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Araps <dylanaraps@users.noreply.github.com>2018-04-01 09:24:38 +1000
committerGitHub <noreply@github.com>2018-04-01 09:24:38 +1000
commite25bc9e2d895f3bbf157cceaf7077c463369ad59 (patch)
treea0792ec2acc4a0c44a758d6dfe3aed243a320e39
parent42338ea59d4456d632077e33ec5aaa6199d79dba (diff)
parent32806bc60fc57e5bb2152f0e485e736eb4f801a1 (diff)
Merge pull request #213 from dylanaraps/backends
backend: initial work to add more backends
-rw-r--r--.pylintrc5
-rw-r--r--.travis.yml4
-rw-r--r--pywal/__main__.py10
-rw-r--r--pywal/backends/__init__.py10
-rw-r--r--pywal/backends/colorthief.py64
-rw-r--r--pywal/backends/colorz.py36
-rw-r--r--pywal/backends/haishoku.py36
-rw-r--r--pywal/backends/schemer2.py34
-rw-r--r--pywal/backends/wal.py87
-rw-r--r--pywal/colors.py176
-rw-r--r--pywal/settings.py1
-rw-r--r--pywal/util.py5
12 files changed, 365 insertions, 103 deletions
diff --git a/.pylintrc b/.pylintrc
index 08f340e..e41fb8c 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -7,7 +7,10 @@ good-names=i,j,k,n,x,y,fg,bg,r,g,b,i3,r1,r2,r3,g1,g2,g3,b1,b2,b3,h,s,v
# too-many-branches:
# Disabled as it's a non-issue and only occurs in the
# process_args() function.
-disable=inconsistent-return-statements,too-many-branches
+# too-many-statements:
+# Disabled as it's a non-issue and only occurs in the
+# process_args() function.
+disable=inconsistent-return-statements,too-many-branches,too-many-statements
[SIMILARITIES]
ignore-imports=y
diff --git a/.travis.yml b/.travis.yml
index 3a0a043..11a9bfd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,16 +7,14 @@ matrix:
- os: linux
python: 3.6
-
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y imagemagick; fi
install:
- - pip install flake8 pylint pyroma
+ - pip install flake8 pylint
script:
- flake8 pywal tests setup.py
- pylint pywal tests setup.py
- - pyroma .
- python setup.py test
diff --git a/pywal/__main__.py b/pywal/__main__.py
index a47deb3..05eea99 100644
--- a/pywal/__main__.py
+++ b/pywal/__main__.py
@@ -36,6 +36,10 @@ def get_args(args):
arg.add_argument("-b", metavar="background",
help="Custom background color to use.")
+ arg.add_argument("--backend", metavar="backend",
+ help="Which color backend to use.",
+ const="list_backends", type=str, nargs="?", default="wal")
+
arg.add_argument("-c", action="store_true",
help="Delete all cached colorschemes.")
@@ -102,6 +106,10 @@ def process_args(args):
reload.colors()
sys.exit(0)
+ if args.backend == "list_backends":
+ print("Available backends:", colors.list_backends())
+ sys.exit(0)
+
if args.q:
sys.stdout = sys.stderr = open(os.devnull, "w")
@@ -120,7 +128,7 @@ def process_args(args):
if args.i:
image_file = image.get(args.i)
- colors_plain = colors.get(image_file, light=args.l)
+ colors_plain = colors.get(image_file, args.l, args.backend)
if args.f:
colors_plain = colors.file(args.f)
diff --git a/pywal/backends/__init__.py b/pywal/backends/__init__.py
new file mode 100644
index 0000000..9a116e8
--- /dev/null
+++ b/pywal/backends/__init__.py
@@ -0,0 +1,10 @@
+r"""
+Hh ____
+HP "HHF:. `._ :.,-'"" "-.
+F F" :::..'"" "-. `.
+F , \ \ "BACKENDS"
+F j\ / ; `. - sorry
+| j `. ` A \
+| | ;_ . 8 \
+J F\_,'| "`-----.\ j `. \
+"""
diff --git a/pywal/backends/colorthief.py b/pywal/backends/colorthief.py
new file mode 100644
index 0000000..180af5f
--- /dev/null
+++ b/pywal/backends/colorthief.py
@@ -0,0 +1,64 @@
+"""
+Generate a colorscheme using ColorThief.
+"""
+import sys
+
+try:
+ from colorthief import ColorThief
+
+except ImportError:
+ print("error: ColorThief wasn't found on your system.",
+ "Try another backend. (wal --backend)")
+ sys.exit(1)
+
+from .. import util
+
+
+def gen_colors(img):
+ """Loop until 16 colors are generated."""
+ color_cmd = ColorThief(img).get_palette
+
+ for i in range(0, 10, 1):
+ raw_colors = color_cmd(color_count=8 + i)
+
+ if len(raw_colors) >= 8:
+ break
+
+ elif i == 19:
+ print("colors: ColorThief couldn't generate a suitable palette",
+ "for the image. Exiting...")
+ sys.exit(1)
+
+ else:
+ print("colors: ColorThief couldn't create a suitable palette, "
+ "trying a larger palette size", 8 + i)
+
+ return [util.rgb_to_hex(color) for color in raw_colors]
+
+
+def adjust(cols, light):
+ """Create palette."""
+ cols.sort(key=util.rgb_to_yiq)
+ raw_colors = [*cols, *cols]
+
+ if light:
+ raw_colors[0] = util.lighten_color(cols[0], 0.90)
+ raw_colors[7] = util.darken_color(cols[0], 0.75)
+
+ else:
+ for color in raw_colors:
+ color = util.lighten_color(color, 0.40)
+
+ raw_colors[0] = util.darken_color(cols[0], 0.80)
+ raw_colors[7] = util.lighten_color(cols[0], 0.60)
+
+ raw_colors[8] = util.lighten_color(cols[0], 0.20)
+ raw_colors[15] = raw_colors[7]
+
+ return raw_colors
+
+
+def get(img, light=False):
+ """Get colorscheme."""
+ cols = gen_colors(img)
+ return adjust(cols, light)
diff --git a/pywal/backends/colorz.py b/pywal/backends/colorz.py
new file mode 100644
index 0000000..0c5148d
--- /dev/null
+++ b/pywal/backends/colorz.py
@@ -0,0 +1,36 @@
+"""
+Generate a colorscheme using Colorz.
+"""
+import shutil
+import subprocess
+import sys
+
+from .. import colors
+from .. import util
+
+
+def gen_colors(img):
+ """Generate a colorscheme using Colorz."""
+ cmd = ["colorz", "-n", "6", "--bold", "0", "--no-preview"]
+ return subprocess.check_output([*cmd, img]).splitlines()
+
+
+def adjust(cols, light):
+ """Create palette."""
+ bg = util.blend_color("#555555", cols[1])
+
+ raw_colors = [bg, *cols, "#FFFFFF",
+ "#333333", *cols, "#FFFFFF"]
+
+ return colors.generic_adjust(raw_colors, light)
+
+
+def get(img, light=False):
+ """Get colorscheme."""
+ if not shutil.which("colorz"):
+ print("error: Colorz wasn't found on your system.",
+ "Try another backend. (wal --backend)")
+ sys.exit(1)
+
+ cols = [col.decode('UTF-8').split()[0] for col in gen_colors(img)]
+ return adjust(cols, light)
diff --git a/pywal/backends/haishoku.py b/pywal/backends/haishoku.py
new file mode 100644
index 0000000..413c9d7
--- /dev/null
+++ b/pywal/backends/haishoku.py
@@ -0,0 +1,36 @@
+"""
+Generate a colorscheme using Haishoku.
+"""
+import sys
+
+try:
+ from haishoku.haishoku import Haishoku
+
+except ImportError:
+ print("error: Haishoku wasn't found on your system.",
+ "Try another backend. (wal --backend)")
+ sys.exit(1)
+
+from .. import colors
+from .. import util
+
+
+def gen_colors(img):
+ """Generate a colorscheme using Colorz."""
+ palette = Haishoku.getPalette(img)
+ return [util.rgb_to_hex(col[1]) for col in palette]
+
+
+def adjust(cols, light):
+ """Create palette."""
+ cols.sort(key=util.rgb_to_yiq)
+ raw_colors = [*cols, *cols]
+ raw_colors[0] = util.lighten_color(cols[0], 0.40)
+
+ return colors.generic_adjust(raw_colors, light)
+
+
+def get(img, light=False):
+ """Get colorscheme."""
+ cols = gen_colors(img)
+ return adjust(cols, light)
diff --git a/pywal/backends/schemer2.py b/pywal/backends/schemer2.py
new file mode 100644
index 0000000..87873ec
--- /dev/null
+++ b/pywal/backends/schemer2.py
@@ -0,0 +1,34 @@
+"""
+Generate a colorscheme using Schemer2.
+"""
+import shutil
+import subprocess
+import sys
+
+from .. import colors
+from .. import util
+
+
+def gen_colors(img):
+ """Generate a colorscheme using Colorz."""
+ cmd = ["schemer2", "-format", "img::colors", "-minBright", "75", "-in"]
+ return subprocess.check_output([*cmd, img]).splitlines()
+
+
+def adjust(cols, light):
+ """Create palette."""
+ cols.sort(key=util.rgb_to_yiq)
+ raw_colors = [*cols[8:], *cols[8:]]
+
+ return colors.generic_adjust(raw_colors, light)
+
+
+def get(img, light=False):
+ """Get colorscheme."""
+ if not shutil.which("schemer2"):
+ print("error: Schemer2 wasn't found on your system.",
+ "Try another backend. (wal --backend)")
+ sys.exit(1)
+
+ cols = [col.decode('UTF-8') for col in gen_colors(img)]
+ return adjust(cols, light)
diff --git a/pywal/backends/wal.py b/pywal/backends/wal.py
new file mode 100644
index 0000000..678c66c
--- /dev/null
+++ b/pywal/backends/wal.py
@@ -0,0 +1,87 @@
+"""
+Generate a colorscheme using imagemagick.
+"""
+import re
+import shutil
+import subprocess
+import sys
+
+from .. import util
+
+
+def imagemagick(color_count, img, magick_command):
+ """Call Imagemagick to generate a scheme."""
+ flags = ["-resize", "25%", "-colors", str(color_count),
+ "-unique-colors", "txt:-"]
+ img += "[0]"
+
+ return subprocess.check_output([*magick_command, img, *flags]).splitlines()
+
+
+def has_im():
+ """Check to see if the user has im installed."""
+ if shutil.which("magick"):
+ return ["magick", "convert"]
+
+ elif shutil.which("convert"):
+ return ["convert"]
+
+ print("error: ImageMagick wasn't found on your system.",
+ "Try another backend. (wal --backend)")
+ sys.exit(1)
+
+
+def gen_colors(img):
+ """Format the output from imagemagick into a list
+ of hex colors."""
+ magick_command = has_im()
+
+ for i in range(0, 20, 1):
+ raw_colors = imagemagick(16 + i, img, magick_command)
+
+ if len(raw_colors) > 16:
+ break
+
+ elif i == 19:
+ print("colors: Imagemagick couldn't generate a suitable palette",
+ "for the image. Exiting...")
+ sys.exit(1)
+
+ else:
+ print("colors: Imagemagick couldn't generate a suitable palette, "
+ "trying a larger palette size", 16 + i)
+
+ return [re.search("#.{6}", str(col)).group(0) for col in raw_colors[1:]]
+
+
+def adjust(colors, light):
+ """Adjust the generated colors and store them in a dict that
+ we will later save in json format."""
+ raw_colors = colors[:1] + colors[8:16] + colors[8:-1]
+
+ # Manually adjust colors.
+ if light:
+ for color in raw_colors:
+ color = util.saturate_color(color, 0.5)
+
+ raw_colors[0] = util.lighten_color(colors[-1], 0.85)
+ raw_colors[7] = colors[0]
+ raw_colors[8] = util.darken_color(colors[-1], 0.4)
+ raw_colors[15] = colors[0]
+
+ else:
+ # Darken the background color slightly.
+ if raw_colors[0][1] != "0":
+ raw_colors[0] = util.darken_color(raw_colors[0], 0.25)
+
+ raw_colors[7] = util.blend_color(raw_colors[7], "#EEEEEE")
+ raw_colors[8] = util.darken_color(raw_colors[7], 0.30)
+ raw_colors[15] = util.blend_color(raw_colors[15], "#EEEEEE")
+
+ return raw_colors
+
+
+def get(img, light=False):
+ """Get colorscheme."""
+ colors = gen_colors(img)
+ return adjust(colors, light)
diff --git a/pywal/colors.py b/pywal/colors.py
index f47ac33..06863ac 100644
--- a/pywal/colors.py
+++ b/pywal/colors.py
@@ -1,114 +1,89 @@
"""
-Generate a colorscheme using imagemagick.
+Generate a palette using various backends.
"""
import os
import re
-import shutil
-import subprocess
import sys
-from .settings import CACHE_DIR, COLOR_COUNT, __cache_version__
from . import util
-
-
-def imagemagick(color_count, img, magick_command):
- """Call Imagemagick to generate a scheme."""
- flags = ["-resize", "25%", "-colors", str(color_count),
- "-unique-colors", "txt:-"]
- img += "[0]"
-
- return subprocess.check_output([*magick_command, img, *flags]).splitlines()
-
-
-def has_im():
- """Check to see if the user has im installed."""
- if shutil.which("magick"):
- return ["magick", "convert"]
-
- elif shutil.which("convert"):
- return ["convert"]
-
- print("error: imagemagick not found, exiting...\n"
- "error: wal requires imagemagick to function.")
- sys.exit(1)
-
-
-def gen_colors(img, color_count):
- """Format the output from imagemagick into a list
- of hex colors."""
- magick_command = has_im()
-
- for i in range(0, 20, 1):
- raw_colors = imagemagick(color_count + i, img, magick_command)
-
- if len(raw_colors) > 16:
- break
-
- elif i == 19:
- print("colors: Imagemagick couldn't generate a suitable scheme",
- "for the image. Exiting...")
- sys.exit(1)
-
- else:
- print("colors: Imagemagick couldn't generate a %s color palette, "
- "trying a larger palette size %s."
- % (color_count, color_count + i))
-
- return [re.search("#.{6}", str(col)).group(0) for col in raw_colors[1:]]
-
-
-def create_palette(img, colors, light):
- """Sort the generated colors and store them in a dict that
- we will later save in json format."""
- raw_colors = colors[:1] + colors[8:16] + colors[8:-1]
-
+from .settings import CACHE_DIR, MODULE_DIR, __cache_version__
+
+
+def list_backends():
+ """List color backends."""
+ return [b.name.replace(".py", "") for b in
+ os.scandir(os.path.join(MODULE_DIR, "backends"))
+ if "__" not in b.name]
+
+
+def colors_to_dict(colors, img):
+ """Convert list of colors to pywal format."""
+ return {
+ "wallpaper": img,
+ "alpha": util.Color.alpha_num,
+
+ "special": {
+ "background": colors[0],
+ "foreground": colors[15],
+ "cursor": colors[1]
+ },
+
+ "colors": {
+ "color0": colors[0],
+ "color1": colors[1],
+ "color2": colors[2],
+ "color3": colors[3],
+ "color4": colors[4],
+ "color5": colors[5],
+ "color6": colors[6],
+ "color7": colors[7],
+ "color8": colors[8],
+ "color9": colors[9],
+ "color10": colors[10],
+ "color11": colors[11],
+ "color12": colors[12],
+ "color13": colors[13],
+ "color14": colors[14],
+ "color15": colors[15]
+ }
+ }
+
+
+def generic_adjust(colors, light):
+ """Generic color adjustment for themers."""
if light:
- # Manually adjust colors.
- raw_colors[7] = raw_colors[0]
- raw_colors[0] = util.lighten_color(raw_colors[15], 0.85)
- raw_colors[15] = raw_colors[7]
- raw_colors[8] = util.lighten_color(raw_colors[7], 0.25)
+ for color in colors:
+ color = util.saturate_color(color, 0.50)
+ color = util.darken_color(color, 0.4)
- else:
- # Darken the background color slightly.
- if raw_colors[0][1] != "0":
- raw_colors[0] = util.darken_color(raw_colors[0], 0.25)
-
- # Manually adjust colors.
- raw_colors[7] = util.blend_color(raw_colors[7], "#EEEEEE")
- raw_colors[8] = util.darken_color(raw_colors[7], 0.30)
- raw_colors[15] = util.blend_color(raw_colors[15], "#EEEEEE")
-
- colors = {"wallpaper": img, "alpha": util.Color.alpha_num,
- "special": {}, "colors": {}}
- colors["special"]["background"] = raw_colors[0]
- colors["special"]["foreground"] = raw_colors[15]
- colors["special"]["cursor"] = raw_colors[15]
-
- if light:
- for i, color in enumerate(raw_colors):
- colors["colors"]["color%s" % i] = util.saturate_color(color, 0.5)
-
- colors["colors"]["color0"] = raw_colors[0]
- colors["colors"]["color7"] = raw_colors[15]
- colors["colors"]["color8"] = util.darken_color(raw_colors[0], 0.5)
- colors["colors"]["color15"] = raw_colors[15]
+ colors[0] = util.lighten_color(colors[0], 0.9)
+ colors[7] = util.darken_color(colors[0], 0.75)
+ colors[8] = util.darken_color(colors[0], 0.25)
+ colors[15] = colors[7]
else:
- for i, color in enumerate(raw_colors):
- colors["colors"]["color%s" % i] = color
+ colors[0] = util.darken_color(colors[0], 0.75)
+ colors[7] = util.lighten_color(colors[0], 0.75)
+ colors[8] = util.lighten_color(colors[0], 0.25)
+ colors[15] = colors[7]
return colors
-def get(img, cache_dir=CACHE_DIR,
- color_count=COLOR_COUNT, light=False):
- """Get the colorscheme."""
- # home_dylan_img_jpg_1.2.2.json
+def cache_fname(img, backend, light, cache_dir):
+ """Create the cache file name."""
color_type = "light" if light else "dark"
- cache_file = re.sub("[/|\\|.]", "_", img)
- cache_file = os.path.join(cache_dir, "schemes", "%s_%s_%s.json"
- % (cache_file, color_type, __cache_version__))
+ file_name = re.sub("[/|\\|.]", "_", img)
+
+ file_parts = [file_name, color_type, backend, __cache_version__]
+ return [cache_dir, "schemes", "%s_%s_%s_%s.json" % (*file_parts,)]
+
+
+def get(img, light=False, backend="wal", cache_dir=CACHE_DIR):
+ """Generate a palette."""
+ # home_dylan_img_jpg_backend_1.2.2.json
+ cache_name = cache_fname(img, backend, light, cache_dir)
+ cache_file = os.path.join(*cache_name)
if os.path.isfile(cache_file):
colors = file(cache_file)
@@ -118,8 +93,15 @@ def get(img, cache_dir=CACHE_DIR,
else:
print("wal: Generating a colorscheme...")
- colors = gen_colors(img, color_count)
- colors = create_palette(img, colors, light)
+ # Dynamically import the backend we want to use.
+ # This keeps the dependencies "optional".
+ try:
+ __import__("pywal.backends.%s" % backend)
+ except ImportError:
+ backend = "wal"
+
+ backend = sys.modules["pywal.backends.%s" % backend]
+ colors = colors_to_dict(getattr(backend, "get")(img, light), img)
util.save_file_json(colors, cache_file)
print("wal: Generation complete.")
diff --git a/pywal/settings.py b/pywal/settings.py
index ca82333..fd12105 100644
--- a/pywal/settings.py
+++ b/pywal/settings.py
@@ -21,5 +21,4 @@ HOME = os.getenv("HOME", os.getenv("USERPROFILE"))
CACHE_DIR = os.path.join(HOME, ".cache", "wal")
MODULE_DIR = os.path.dirname(__file__)
CONF_DIR = os.path.join(HOME, ".config", "wal")
-COLOR_COUNT = 16
OS = platform.uname()[0]
diff --git a/pywal/util.py b/pywal/util.py
index 43ee3fb..65d4557 100644
--- a/pywal/util.py
+++ b/pywal/util.py
@@ -144,6 +144,11 @@ def saturate_color(color, amount):
return rgb_to_hex((int(r), int(g), int(b)))
+def rgb_to_yiq(color):
+ """Sort a list of colors."""
+ return colorsys.rgb_to_yiq(*hex_to_rgb(color))
+
+
def disown(cmd):
"""Call a system command in the background,
disown it and hide it's output."""