├── .gitignore ├── LICENSE ├── README.md ├── generator.py ├── screenshot.png ├── template-dark ├── template-light └── themes.xresources ├── dawn.xresources ├── hund.xresources └── terminal-sexy.xresources /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marco Gullo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kolor-generator 2 | 3 | ![Screenshot](screenshot.png) 4 | 5 | This script generates a KDE Plasma color scheme from an xresource color palette, like the ones you can generate from [terminal.sexy](https://terminal.sexy). 6 | 7 | Requires [Python 3](https://www.python.org/). 8 | 9 | # Basic usage 10 | 11 | Clone this repository in a directory of your choice. 12 | 13 | Place the color schemes in .xresources format in "themes.xresources/". Some demo schemes are already available in the folder. 14 | 15 | Then **run the command**: 16 | 17 | `python generator.py generate-all` 18 | 19 | This will generate a Plasma color theme for each .xresources file and will place them in `~/.local/share/color-schemes/`. Now you just need to go to *System Settings -> Colors* to select the color scheme just created. 20 | 21 | If you want to generate the color scheme **just for one specific .xresources file**, run: 22 | 23 | `python generator.py generate FILENAME` (without .xresources) 24 | 25 | If you want to generate a **light color scheme**, put `-l` at the end of the command: 26 | 27 | `python generator.py generate-all -l` 28 | 29 | # How does it (simply) work 30 | 31 | The script uses the two files "template-dark" and "template-light" to generate respectively dark and light schemes. 32 | 33 | It does so by replacing each color with the closest color from the palette obtained from the .xresources file. 34 | 35 | This means that the colors in the template affect the final result, which depends also on the number and variety of colors in the .xresources file palette. 36 | -------------------------------------------------------------------------------- /generator.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | import os 4 | from math import sqrt 5 | 6 | COLOR_SCHEME_DIR = os.path.expanduser('~') + '/.local/share/color-schemes/' 7 | PALETTE_DIR = './themes.xresources/' 8 | 9 | 10 | class Color: 11 | def __init__(self, col): 12 | if re.match('.+,.+,.+', col): 13 | rgb = [int(c) for c in col.split(',')] 14 | else: 15 | rgb = [int(col[i:i+2], 16) for i in range(0, 6, 2)] 16 | self.r, self.g, self.b = rgb 17 | 18 | def distance(self, other): 19 | return sqrt((self.r-other.r)**2 + (self.g-other.g)**2 + (self.b-other.b)**2) 20 | 21 | def closest(self, palette): 22 | closest_distance = None 23 | closest_color = None 24 | for color in palette.colors: 25 | distance = self.distance(color) 26 | if closest_distance is None or distance < closest_distance: 27 | closest_distance = distance 28 | closest_color = color 29 | return closest_color 30 | 31 | def get_text(self, hex=False): 32 | if hex: 33 | return '#{:0>6}'.format(format(self.r*16**4 + self.g*16**2 + self.b, 'x')) 34 | else: 35 | return '{},{},{}'.format(self.r, self.g, self.b) 36 | 37 | 38 | class Palette: 39 | def __init__(self, xres): 40 | self.colors = [] 41 | for line in xres.splitlines(): 42 | if re.match('.*#.*', line): 43 | self.colors.append(Color(line.split('#')[1])) 44 | 45 | 46 | class Line: 47 | def __init__(self, text): 48 | if re.match('.*=.*', text) and re.match('.+,.+,.+', text.split('=')[1]): 49 | self.prefix = text.split('=')[0] + '=' 50 | self.color = Color(text.split('=')[1]) 51 | elif re.match('.*=#.*', text): 52 | self.prefix = text.split('#')[0] 53 | self.color = Color(text.split('#')[1]) 54 | else: 55 | self.prefix = text 56 | self.color = None 57 | 58 | def matches(self, regexp): 59 | return re.match(regexp, self.prefix) 60 | 61 | def change_val(self, new): 62 | self.prefix = self.prefix.split('=')[0] + '=' + new 63 | 64 | def set_palette(self, palette): 65 | if self.color is not None: 66 | self.color = self.color.closest(palette) 67 | 68 | def get_text(self, hex=False): 69 | return self.prefix if self.color is None else self.prefix + self.color.get_text(hex) 70 | 71 | 72 | class Document: 73 | def __init__(self, text): 74 | self.lines = [Line(line) for line in text.splitlines()] 75 | 76 | def get_document(self, hex=False): 77 | return '\n'.join([line.get_text(hex) for line in self.lines]) 78 | 79 | def change_theme_name(self, name): 80 | for line in self.lines: 81 | if line.matches('ColorScheme=.*') or line.matches('Name=.*'): 82 | line.change_val(name) 83 | 84 | def set_palette(self, palette): 85 | for line in self.lines: 86 | line.set_palette(palette) 87 | 88 | def to_file(self, path, hex=False): 89 | with open(path, 'w') as f: 90 | f.write(self.get_document(hex)) 91 | 92 | 93 | op = sys.argv[1] 94 | 95 | # python generator.py generate-all (opt -l) 96 | if op == 'generate-all': 97 | light = len(sys.argv) > 2 and sys.argv[2] == '-l' 98 | template_name = 'template-light' if light else 'template-dark' 99 | with open(template_name, 'r') as f: 100 | template = f.read() 101 | for file_name in os.listdir(PALETTE_DIR): 102 | if file_name.endswith('.xresources'): 103 | theme_name = file_name.replace('.xresources', '') 104 | doc = Document(template) 105 | with open(PALETTE_DIR + file_name, 'r') as f: 106 | pal = Palette(f.read()) 107 | doc.set_palette(pal) 108 | suffix = ' light' if light else ' dark' 109 | doc.change_theme_name(theme_name + suffix) 110 | doc.to_file(COLOR_SCHEME_DIR + theme_name + suffix + '.colors') 111 | print('Done') 112 | 113 | # python generator.py generate theme (opt -l) 114 | if op == 'generate': 115 | light = len(sys.argv) > 2 and sys.argv[2] == '-l' 116 | template_name = 'template-light' if light else 'template-dark' 117 | theme_name = sys.argv[2] 118 | with open(PALETTE_DIR + theme_name + '.xresources', 'r') as f: 119 | palette = Palette(f.read()) 120 | with open(template_name, 'r') as f: 121 | doc = Document(f.read()) 122 | doc.set_palette(palette) 123 | suffix = ' light' if light else ' dark' 124 | doc.change_theme_name(theme_name + suffix) 125 | doc.to_file(COLOR_SCHEME_DIR + theme_name + suffix + '.colors') 126 | print('Done') 127 | 128 | # python scheme-generator.py copy-hex theme 129 | if op == 'copy-hex': 130 | name = sys.argv[2] 131 | path = name + '.colors' 132 | with open(path, 'r') as f: 133 | doc = Document(f.read()) 134 | doc.to_file(name + '-hex', hex=True) 135 | print('Done') 136 | 137 | # python generator.py hex2theme theme 138 | if op == 'hex2theme': 139 | name = sys.argv[2] 140 | path = name + '-hex' 141 | with open(path, 'r') as f: 142 | doc = Document(f.read()) 143 | doc.to_file(name + '.colors') 144 | print('Done') 145 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gullmar/kolor-generator/3a7bc2f4b4fbe79461ff56868b0c7cb1fffae802/screenshot.png -------------------------------------------------------------------------------- /template-dark: -------------------------------------------------------------------------------- 1 | [ColorEffects:Disabled] 2 | Color=#383838 3 | ColorAmount=0 4 | ColorEffect=0 5 | ContrastAmount=0.65 6 | ContrastEffect=1 7 | IntensityAmount=0.1 8 | IntensityEffect=2 9 | 10 | [ColorEffects:Inactive] 11 | ChangeSelectionColor=true 12 | Color=#706f6e 13 | ColorAmount=0.025 14 | ColorEffect=2 15 | ContrastAmount=0.1 16 | ContrastEffect=2 17 | Enable=false 18 | IntensityAmount=0 19 | IntensityEffect=0 20 | 21 | [Colors:Button] 22 | BackgroundAlternate=#4d4d4d 23 | BackgroundNormal=#1d1f21 24 | DecorationFocus=#3daee9 25 | DecorationHover=#3daee9 26 | ForegroundActive=#3daee9 27 | ForegroundInactive=#bdc3c7 28 | ForegroundLink=#2980b9 29 | ForegroundNegative=#da4453 30 | ForegroundNeutral=#f67400 31 | ForegroundNormal=#eff0f1 32 | ForegroundPositive=#27ae60 33 | ForegroundVisited=#7f8c8d 34 | 35 | [Colors:Selection] 36 | BackgroundAlternate=#1d99f3 37 | BackgroundNormal=#1d99f3 38 | DecorationFocus=#1d99f3 39 | DecorationHover=#1d99f3 40 | ForegroundActive=#fcfcfc 41 | ForegroundInactive=#eff0f1 42 | ForegroundLink=#fdbc4b 43 | ForegroundNegative=#da4453 44 | ForegroundNeutral=#f67400 45 | ForegroundNormal=#eff0f1 46 | ForegroundPositive=#27ae60 47 | ForegroundVisited=#bdc3c7 48 | 49 | [Colors:Tooltip] 50 | BackgroundAlternate=#4d4d4d 51 | BackgroundNormal=#1d1f21 52 | DecorationFocus=#3daee9 53 | DecorationHover=#3daee9 54 | ForegroundActive=#3daee9 55 | ForegroundInactive=#bdc3c7 56 | ForegroundLink=#2980b9 57 | ForegroundNegative=#da4453 58 | ForegroundNeutral=#f67400 59 | ForegroundNormal=#eff0f1 60 | ForegroundPositive=#27ae60 61 | ForegroundVisited=#7f8c8d 62 | 63 | [Colors:View] 64 | BackgroundAlternate=#1d1f21 65 | BackgroundNormal=#000000 66 | DecorationFocus=#3daee9 67 | DecorationHover=#3daee9 68 | ForegroundActive=#3daee9 69 | ForegroundInactive=#bdc3c7 70 | ForegroundLink=#2980b9 71 | ForegroundNegative=#da4453 72 | ForegroundNeutral=#f67400 73 | ForegroundNormal=#eff0f1 74 | ForegroundPositive=#27ae60 75 | ForegroundVisited=#7f8c8d 76 | 77 | [Colors:Window] 78 | BackgroundAlternate=#4d4d4d 79 | BackgroundNormal=#1d1f21 80 | DecorationFocus=#3daee9 81 | DecorationHover=#3daee9 82 | ForegroundActive=#3daee9 83 | ForegroundInactive=#bdc3c7 84 | ForegroundLink=#2980b9 85 | ForegroundNegative=#da4453 86 | ForegroundNeutral=#f67400 87 | ForegroundNormal=#eff0f1 88 | ForegroundPositive=#27ae60 89 | ForegroundVisited=#7f8c8d 90 | 91 | [Colors:Complementary] 92 | BackgroundAlternate=#3b4045 93 | BackgroundNormal=#1d1f21 94 | DecorationFocus=#1e92ff 95 | DecorationHover=#3daee6 96 | ForegroundActive=#f67400 97 | ForegroundInactive=#afb0b3 98 | ForegroundLink=#3daee6 99 | ForegroundNegative=#ed1515 100 | ForegroundNeutral=#c9ce3b 101 | ForegroundNormal=#eff0f1 102 | ForegroundPositive=#11d116 103 | ForegroundVisited=#3daee6 104 | 105 | [General] 106 | ColorScheme=Breeze Dark 107 | Name=Breeze Dark 108 | shadeSortColumn=true 109 | 110 | [KDE] 111 | contrast=4 112 | 113 | [WM] 114 | activeBackground=#1d1f21 115 | activeBlend=#ffffff 116 | activeForeground=#eff0f1 117 | inactiveBackground=#1d1f21 118 | inactiveBlend=#4b4743 119 | inactiveForeground=#7f8c8d -------------------------------------------------------------------------------- /template-light: -------------------------------------------------------------------------------- 1 | [ColorEffects:Disabled] 2 | Color=#383838 3 | ColorAmount=0 4 | ColorEffect=0 5 | ContrastAmount=0.65 6 | ContrastEffect=1 7 | IntensityAmount=0.1 8 | IntensityEffect=2 9 | 10 | [ColorEffects:Inactive] 11 | ChangeSelectionColor=true 12 | Color=#706f6e 13 | ColorAmount=0.025 14 | ColorEffect=2 15 | ContrastAmount=0.1 16 | ContrastEffect=2 17 | Enable=false 18 | IntensityAmount=0 19 | IntensityEffect=0 20 | 21 | [Colors:Button] 22 | BackgroundAlternate=#bdc3c7 23 | BackgroundNormal=#eff0f1 24 | DecorationFocus=#3daee9 25 | DecorationHover=#93cee9 26 | ForegroundActive=#3daee9 27 | ForegroundInactive=#7f8c8d 28 | ForegroundLink=#2980b9 29 | ForegroundNegative=#da4453 30 | ForegroundNeutral=#f67400 31 | ForegroundNormal=#232627 32 | ForegroundPositive=#27ae60 33 | ForegroundVisited=#7f8c8d 34 | 35 | [Colors:Selection] 36 | BackgroundAlternate=#1d99f3 37 | BackgroundNormal=#3daee9 38 | DecorationFocus=#3daee9 39 | DecorationHover=#93cee9 40 | ForegroundActive=#fcfcfc 41 | ForegroundInactive=#eff0f1 42 | ForegroundLink=#fdbc4b 43 | ForegroundNegative=#da4453 44 | ForegroundNeutral=#f67400 45 | ForegroundNormal=#fcfcfc 46 | ForegroundPositive=#27ae60 47 | ForegroundVisited=#bdc3c7 48 | 49 | [Colors:Tooltip] 50 | BackgroundAlternate=#4d4d4d 51 | BackgroundNormal=#232627 52 | DecorationFocus=#3daee9 53 | DecorationHover=#93cee9 54 | ForegroundActive=#3daee9 55 | ForegroundInactive=#bdc3c7 56 | ForegroundLink=#2980b9 57 | ForegroundNegative=#da4453 58 | ForegroundNeutral=#f67400 59 | ForegroundNormal=#fcfcfc 60 | ForegroundPositive=#27ae60 61 | ForegroundVisited=#7f8c8d 62 | 63 | [Colors:View] 64 | BackgroundAlternate=#eff0f1 65 | BackgroundNormal=#fcfcfc 66 | DecorationFocus=#3daee9 67 | DecorationHover=#93cee9 68 | ForegroundActive=#3daee9 69 | ForegroundInactive=#7f8c8d 70 | ForegroundLink=#2980b9 71 | ForegroundNegative=#da4453 72 | ForegroundNeutral=#f67400 73 | ForegroundNormal=#232627 74 | ForegroundPositive=#27ae60 75 | ForegroundVisited=#7f8c8d 76 | 77 | [Colors:Window] 78 | BackgroundAlternate=#bdc3c7 79 | BackgroundNormal=#eff0f1 80 | DecorationFocus=#3daee9 81 | DecorationHover=#93cee9 82 | ForegroundActive=#3daee9 83 | ForegroundInactive=#7f8c8d 84 | ForegroundLink=#2980b9 85 | ForegroundNegative=#da4453 86 | ForegroundNeutral=#f67400 87 | ForegroundNormal=#232627 88 | ForegroundPositive=#27ae60 89 | ForegroundVisited=#7f8c8d 90 | 91 | [Colors:Complementary] 92 | BackgroundAlternate=#3b4045 93 | BackgroundNormal=#31363b 94 | DecorationFocus=#1e92ff 95 | DecorationHover=#3daee6 96 | ForegroundActive=#93cee9 97 | ForegroundInactive=#afb0b3 98 | ForegroundLink=#3daee6 99 | ForegroundNegative=#e74c3c 100 | ForegroundNeutral=#fdbc4b 101 | ForegroundNormal=#eff0f1 102 | ForegroundPositive=#2ecc71 103 | ForegroundVisited=#3daee6 104 | 105 | [General] 106 | ColorScheme=Breeze 107 | Name=Breeze 108 | shadeSortColumn=true 109 | 110 | [KDE] 111 | contrast=4 112 | 113 | [WM] 114 | activeBackground=#475057 115 | activeBlend=#fcfcfc 116 | activeForeground=#fcfcfc 117 | inactiveBackground=#eff0f1 118 | inactiveBlend=#4b4743 119 | inactiveForeground=#bdc3c7 -------------------------------------------------------------------------------- /themes.xresources/dawn.xresources: -------------------------------------------------------------------------------- 1 | ! special 2 | *.foreground: #9b9081 3 | *.background: #181b20 4 | *.cursorColor: #9b9081 5 | 6 | ! black 7 | *.color0: #353535 8 | *.color8: #5f5f5f 9 | 10 | ! red 11 | *.color1: #744b40 12 | *.color9: #785850 13 | 14 | ! green 15 | *.color2: #6d6137 16 | *.color10: #6f6749 17 | 18 | ! yellow 19 | *.color3: #765636 20 | *.color11: #776049 21 | 22 | ! blue 23 | *.color4: #61564b 24 | *.color12: #696057 25 | 26 | ! magenta 27 | *.color5: #6b4a49 28 | *.color13: #6f5a59 29 | 30 | ! cyan 31 | *.color6: #435861 32 | *.color14: #525f66 33 | 34 | ! white 35 | *.color7: #b3b3b3 36 | *.color15: #cdcdcd 37 | -------------------------------------------------------------------------------- /themes.xresources/hund.xresources: -------------------------------------------------------------------------------- 1 | ! special 2 | *.foreground: #ffffff 3 | *.background: #161616 4 | *.cursorColor: #ffffff 5 | 6 | ! black 7 | *.color0: #222222 8 | *.color8: #666666 9 | 10 | ! red 11 | *.color1: #e84f4f 12 | *.color9: #d23d3d 13 | 14 | ! green 15 | *.color2: #b7ce42 16 | *.color10: #bde077 17 | 18 | ! yellow 19 | *.color3: #fea63c 20 | *.color11: #ffe863 21 | 22 | ! blue 23 | *.color4: #66aabb 24 | *.color12: #aaccbb 25 | 26 | ! magenta 27 | *.color5: #b7416e 28 | *.color13: #e16a98 29 | 30 | ! cyan 31 | *.color6: #6d878d 32 | *.color14: #42717b 33 | 34 | ! white 35 | *.color7: #dddddd 36 | *.color15: #cccccc 37 | -------------------------------------------------------------------------------- /themes.xresources/terminal-sexy.xresources: -------------------------------------------------------------------------------- 1 | ! special 2 | *.foreground: #c5c8c6 3 | *.background: #1d1f21 4 | *.cursorColor: #c5c8c6 5 | 6 | ! black 7 | *.color0: #282a2e 8 | *.color8: #373b41 9 | 10 | ! red 11 | *.color1: #a54242 12 | *.color9: #cc6666 13 | 14 | ! green 15 | *.color2: #8c9440 16 | *.color10: #b5bd68 17 | 18 | ! yellow 19 | *.color3: #de935f 20 | *.color11: #f0c674 21 | 22 | ! blue 23 | *.color4: #5f819d 24 | *.color12: #81a2be 25 | 26 | ! magenta 27 | *.color5: #85678f 28 | *.color13: #b294bb 29 | 30 | ! cyan 31 | *.color6: #5e8d87 32 | *.color14: #8abeb7 33 | 34 | ! white 35 | *.color7: #707880 36 | *.color15: #e1e2e3 37 | --------------------------------------------------------------------------------