/kle-colouriser

A small shell script for applying colouring rules to KLE files

Primary LanguagePythonGNU General Public License v3.0GPL-3.0

KLE-Colouriser

A small shell script for applying colouring rules to KLE files. When paired with keycov, it makes it much easier to make any insane colourway-pattern compatible with a diverse range of keyboards.

This script can also be used to create a single source of truth for colourising information.

Table of Contents

Setup

You’ll need a working installation of python3 and its package manager pip3. Download and unzip kle-colouriser.zip from the latest release then open up a terminal and type the following commands. (These assume that the archive was unzipped in ~/Downloads/kle-colouriser/.)

cd ~/Downloads/kle-colouriser/
pip3 install -r requirements.txt
python3 kle-colouriser.py --help

This last step should print out the usage information for kle-colouriser.

Usage

Running the thing

Before running, kle-colouriser will require two things:

  1. A colour-map file to apply (this guide assumes examples/colour-map.yml)
  2. A folderful of kle files to generate (we’ll call this keebs/)

To run kle-colouriser, open a terminal and type the following (assuming that the latest release was unzipped to ~/Downloads/kle-colouriser/.)

cd ~/Downloads/kle-colouriser/
python3 kle-colouriser.py examples/colour-map.yml examples/keebs/ colourised-keebs/

This will apply the example colour-map to the example layouts and output the results in the (new) colourised-keebs/ directory.

If running on Linux or macOS, it is possible to make the python3 command above somewhat shorter by downloading the kle-colouriser binary from the latest release and replacing the python3 kle_colouriser.py above with ./kle-colouriser. The file ./kle-colouriser is just a zip archive of the source which can be run by python anywhere (assuming it can access its dependencies).

Configuring colour-maps

Colour-maps are just a list of simple condition-action rules written in the quite specific (and fairly common) markup language, yaml. Each colour-map rule specifies: its name, a colour to apply to caps, a colour to apply to legends and a set of conditions which must be satisfied in order to apply the rule. Each keycap in a KLE layout is then checked against the colour-map rules and the first with all conditions satisfied is then applied to that cap. (That is, if two rules have satisfied conditions, that which appears higher in the colour-map file is used.)

A detailed account of kle-colouriser’s decisions can be found by increasing the verbosity level. Passing --verbosity=1 will show the rule chosen for each key in each layout. Passing --verbosity=2 will show the results of evaluating each condition within each rule for each key in each layout (there’s a lot of output).

The following is an example colour-map.

- name: specials
  cap-colour: 'ffff00'
  glyph-style: '#ff8000'
  key-name:
  - Esc
  - Enter
- name: alphas
  cap-colour: '00ff00'
  glyph-style: '#ff0000;'
  key-pos: x + y <= 15
  key-name:
  - .-.+
  - '[^+/*-]'
  - ''
  - F[1-49][0-2]?
- name: something-else
  cap-colour: '123456'
  glyph-style: '123456'
  key-pos: 2 * x + 3 * y >= 10
- name: mods
  cap-colour: 'ff0000'
  glyph-style: '00ff00'
  key-name:
  - .*

The key-pos condition

It is possible to make the colour of the key depend on its position in a given layout. For this, a mathematical condition may be specified. Note that as per the KLE standard, the value of x increases to the right, and the value of y downwards.

Conditions are constructed as follows, closely following Python+C syntax.

  • Numbers and the variables x and y are valid expressions
  • The negation of an expression is also an expression
  • A pair of expressions can be compared by any of the following binary operations.
    • ^^ power
    • *, multiplication
    • /, division, e.g. 3 / 2 = 1.5
    • //, integer division, e.g. 3 // 2 = 1
    • %, modulo, remainder of integer division, e.g. 3 % 2 = 1
    • +, addition
    • -, subtraction
  • A pair of expressions may be compared to form a condition
    • < less-than
    • <=, less-than-or-equal
    • >, greater-than
    • >=, greater-than-or-equal
    • =, equal
    • ==, equal
    • !=, not equal
  • A pair of conditions may be used in logical operations; this is also a condition:
    • ! logical negation (true iff its input is false)
    • & logical conjunction (true iff both inputs are true)
    • | logical disjunction (true iff either input is true)
    • ^ exclusive or (true iff either but not both inputs is true)
    • => logical implication (true iff either the left is false, or both are true)
    • <=> logical equivalence (true iff both inputs have the same value)

If a conditional operator is omitted, a value of true is returned if and only if the numerical value resolved is not equal to zero.

Note that as only binary operations are considered (exactly two inputs), multiple comparisons can have unexpected results, such as the following.

0.3 < 0.1 < 0.2
= (0.3 < 0.1) < 0.2
= False < 0.2
= 0 < 0.2
= True

Use the & operator instead: a < b < c should be expanded to a < b & b < c.

The key-name condition

It is possible for the legend text of a key to be used to constrain colour-application. The contents of the key-name field is either a string, or a list of strings, each of which represents a regular expression. If the keycap legend matches any of the regular expressions specified, then the condition is true. Any new-line characters present in the keycap legend are replaced by dashes, for example !\n1 becomes !-1.

It’s not essential to know regular expressions, but a few basics such as those outlined below can make things a little more streamlined. The cheatsheet and playground on regexr may be helpful.

Consider the example regular expression .*. This comprises of ., which means match any one character, and * which means repeat zero or more of the regular expression to its left. Hence .* just means that anything, including the empty string, is matched.

Similarly, the regex [0-9] specifies a character class which means match any one of the characters specified inside it. Here a range from 0 to 9 is specified, hence this will match any single digit. In the example colour-map, this concept is used in conjunction with the ? operator which matches either zero or one_ of the regular expression to its left. This was used in the expression F[1-49][0-2]?, which when applied to a 100% keyboard will match keys F1 to F4 and F9 to F12.

The layout-file-name condition

It is possible to apply the same rule differently depending on the layout file to which it is applied. This is done using regular expressions in the same way as the key-name condition. If this is required, some diligent naming if input files may be useful. For example, orthogonal layouts might have the word ortho as a part of their name, which would be matched by .*ortho.*.

The all condition

Takes a set of conditions as specified in this readme. Returns true iff every single one of its sub-conditions are true.

The any condition

Takes a set of conditions as specified in this readme. Returns true iff at least one of its sub-conditions are true.

The not-all condition

Takes a set of conditions as specified in this readme. Negation of the all condition, returns true iff at least one of its sub-conditions is false.

The not-any condition

Takes a set of conditions as specified in this readme. Negation of the any condition, returns true iff none of its sub-conditions is true.

Caveats

Please note that given the quite organic nature of many keyboard layouts, some colour-map rules may be applicable to some but not others, and as such a human touch may occasionally be required. For example, a 1u-wide stripe of colour at a 30° angle may work well for a 100% keyboard, but for an orthogonal one, a 45° angle may be required for the same visual effect due to aliasing (kle-colouriser will not support anti-aliasing). To some extent it may be possible to adapt the colour-map rules slightly e.g. by using different rules with layout-file-name conditions to react differently to problematic layout files.

The decision of whether to perfect already-good colour-map files or to simply amend problems by hand will entirely depend on the user’s workflow. In the latter case, assuming reasonable colour-maps, the amount of work to fix these issues is likely less than that required to colourise the inputted layouts entirely manually.

Author

This code was written by Ed Jones (Discord @kcza#4691).