Skip to content

Commit

Permalink
Merge pull request #5 from Max-Derner/maxd
Browse files Browse the repository at this point in the history
Usable documentation
  • Loading branch information
Max-Derner authored Nov 30, 2024
2 parents 6d8f997 + bcf5eb0 commit 6b19e65
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 3 deletions.
207 changes: 205 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,214 @@

![promo image](./README_IMGS/promo_img.png)

This a a project to develop a Python PyPI package that not just handles colour text output but can also apply sweet colour effects to your text.
This a project to develop a Python PyPI package that not just handles colour text output but can also apply colour effects such as gradients to your text.


# Introduction

This package uses ANSI escape sequence select graphics renditions in order to apply colour and style to text and backgrounds.
How is this package any different to the others?
* 8 bit codes as well as 4 bit codes
* there is an ever expanding library of effects that can be applied to larger blocks of texts.
* super slick and simple interface


#### N.B.
Different terminals render colours differently, some terminals don't even render ANSI escape sequences at all, leading to a big mess of text. Playing around with ANSI escape codes is just that, it is playing.

Some basic colours used sparingly to highlight different logging levels is fine but you can't get carried away with colours in serious projects.

# Getting started

First, there 4 bit and 8 bit codes available, import from `colour_fx.four_bit` and `colour_fx.eight_bit` respectively. However, both 8 bit and 4 bit codes can be used together without issue.

#### N.B.
The objects in these modules only provide the the value to be used inside an ANSI escape sequence. They will need to be compiled into an ANSI escape sequence using `compile_ansi_code` which can be imported with `from colour_fx import compile_ansi_code`.

## 4 bit example
```python
from colour_fx import compile_ansi_code
from colour_fx.four_bit import Colour, Style

blinking_red_on_yellow = compile_ansi_code(
# add as many ANSI values as you like and receive a single ANSI
# escape sequence to handle all effects
Colour.RED,
Colour.YELLOW.bright_background,
Style.BLINK
)

# an empty call to compile_ansi_code() gives the reset ANSI escape
# sequence which resets the way text is rendered
RESET = compile_ansi_code()

print(F"HELLO, {blinking_red_on_yellow}WORLD{RESET}!")
```
![Colour example output](./README_IMGS/Colour-example-output.gif)

## 8 bit examples

##### N.B.
8 bit colours are much more unpredictable in appearance between different terminals than 4 bit colours are.

### `SimpleColour`
```python
from colour_fx import compile_ansi_code
from colour_fx.eight_bit import SimpleColour

red_on_yellow = compile_ansi_code(
# add as many ANSI values as you like and receive a single ANSI
# escape sequence to handle all effects
SimpleColour.RED,
SimpleColour.YELLOW.bright_background,
)

# an empty call to compile_ansi_code() gives the reset ANSI escape
# sequence which resets the way text is rendered
RESET = compile_ansi_code()

print(F"HELLO, {red_on_yellow}WORLD{RESET}!")
```
#### output
![SipleColour example output](./README_IMGS/SimpleColour-example-output.png)

### `Grey`
```python
from colour_fx import compile_ansi_code
from colour_fx.eight_bit import Grey

# Grey has 24 different shades available, here we use 12 to apply a
# gradient to the text "HELLO, WORLD!"
grey_array = [
Grey.AA,
Grey.AC,
Grey.AE,
Grey.BB,
Grey.BD,
Grey.BF,
Grey.CA,
Grey.CC,
Grey.CE,
Grey.DB,
Grey.DD,
Grey.DF,
]

# converting shades in grey_array to background colours and
# reversing order
grey_back = [grey.background for grey in reversed(grey_array)]

# Compiling the values in grey_array and grey_back into codes
g = [
compile_ansi_code(
grey_array[idx],
grey_back[idx],
)
for idx in range(len(grey_array))
]

# an empty call to compile_ansi_code() gives the reset ANSI escape
# sequence which resets the way text is rendered
RESET = compile_ansi_code()

# Accessing the individual ANSI escape codes in the list of codes to
# create a gradient
print(
F"{g[0]}H{g[1]}E{g[2]}L{g[3]}L{g[4]}O{g[5]},"
F"{g[6]} W{g[7]}O{g[8]}R{g[9]}L{g[10]}D{g[11]}!{RESET}"
)
```
#### output
![Grey example output](./README_IMGS/Grey-example-output.png)

### `RGB`
```python
from colour_fx import compile_ansi_code
from colour_fx.eight_bit import RGB


# RGB is different to the rest in that you need to pass values in and
# initialise the object rather than treating as an Enum
# RGB values should be an integer between 0 and 5 inclusive
spectrum = [
RGB(5, 0, 0).foreground,
RGB(3, 2, 0).foreground,
RGB(1, 4, 0).foreground,
RGB(0, 4, 1).foreground,
RGB(0, 2, 3).foreground,
RGB(0, 0, 5).foreground,
]

# compiling spectrum into ANSI escape sequences while adding a
# white background
s = [
compile_ansi_code(spec, RGB(5, 5, 5).background)
for spec in spectrum
]

# an empty call to compile_ansi_code() gives the reset ANSI escape
# sequence which resets the way text is rendered
RESET = compile_ansi_code()

# Accessing individual elements of s allows a gradient
print(F"{s[0]}HE{s[1]}LL{s[2]}O,{s[3]} WO{s[4]}RL{s[5]}D!{RESET}")
```

#### output
![RGB output](./README_IMGS/RGB-example-output.png)


# Getting advanced
Now this is what inspired the creation of this package, being able to apply gradients to large blocks of text.

## gradients
```python
from colour_fx.four_bit import Colour
from colour_fx.effects.gradients import create_vertical_gradient_field
from colour_fx.effects import apply_ansi_field

rainbow_vals = [
[Colour.RED],
[Colour.RED.bright],
[Colour.YELLOW.bright],
[Colour.GREEN],
[Colour.BLUE],
[Colour.BLUE.bright],
[Colour.MAGENTA],
]

text_to_render = (
" ::: ::: :::::::::: ::: ::: :::::::: ::: \n"
" :+: :+: :+: :+: :+: :+: :+: :+: \n"
" +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ \n"
" +#++:++#++ +#++:++# +#+ +#+ +#+ +:+ +#+ \n"
" +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ \n"
" #+# #+# #+# #+# #+# #+# #+# \n"
" ### ### ########## ########## ########## ######## ### \n"
)

output_field = create_vertical_gradient_field(
template=text_to_render,
ansi_vals=rainbow_vals,
indent=3,
step=1,
)

output_text = apply_ansi_field(
text=text_to_render,
field=output_field
)

print(output_text)
```
#### output
![vertical gradient example output](./README_IMGS/vertical-gradient-example-output.png)


# Working on this?
Don't use Windows, install Python 3.9, then use `source ./tooling && venv-setup` to get yourself a venv set up with Python 3.9 (our lowest supported Python version)

Use command `lint-cfx` to lint work, and command `test-cfx` to test work.

Use a branch in your name to do feature work, then submit a pull request.
Use a branch in your name to do feature work, then submit a pull request.

Binary file added README_IMGS/Colour-example-output.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README_IMGS/Grey-example-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README_IMGS/RGB-example-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README_IMGS/SimpleColour-example-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README_IMGS/vertical-gradient-example-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/colour_fx/eight_bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def background(self) -> ansi_val:


class RGB:
"""Accepts rgb values between 0 and 6 inclusive, then produces ANSI
"""Accepts rgb values between 0 and 5 inclusive, then produces ANSI
values when requested via the foreground and background properties"""
def __init__(self,
r: Literal[0, 1, 2, 3, 4, 5],
Expand Down
21 changes: 21 additions & 0 deletions src/colour_fx/four_bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
# https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands
# Skipping lots of stuff that's not widely supported
class Style(str, Enum):
"""Produces a 4 bit ANSI style code value which can be inserted
into an ANSI escape sequence.
Example:
```python
blinking = Style.BLINK
```"""
BOLD = '1'
FAINT = '2'
UNDERLINE = '4'
Expand All @@ -25,6 +33,19 @@ class Style(str, Enum):


class Colour(str, Enum):
"""Produces a 4 bit ANSI colour code value which can be inserted
into an ANSI escape sequence.
Produces code for foreground colour by default. Access properties
`bright`, `background`, and `bright_background` in order to modify
as appropriate.
Example:
```python
red_foreground = Colour.RED
blue_background = Colour.BLUE.background
"""
BLACK = '30'
RED = '31'
GREEN = '32'
Expand Down

0 comments on commit 6b19e65

Please sign in to comment.