19 Commits

Author SHA1 Message Date
2b054c4ffe Update dependency build to v1
All checks were successful
Tests / test (3.10) (pull_request) Successful in 1m30s
Tests / test (3.11) (pull_request) Successful in 1m35s
Tests / test (3.8) (pull_request) Successful in 1m29s
Tests / test (3.9) (pull_request) Successful in 1m29s
2024-01-21 22:14:08 +02:00
c9e16ea6da Update dependency mypy to v1.8.0
All checks were successful
Tests / test (3.10) (pull_request) Successful in 1m23s
Tests / test (3.11) (pull_request) Successful in 1m26s
Tests / test (3.8) (pull_request) Successful in 1m24s
Tests / test (3.9) (pull_request) Successful in 1m24s
Tests / test (3.10) (push) Successful in 1m54s
Tests / test (3.11) (push) Successful in 1m36s
Tests / test (3.8) (push) Successful in 1m32s
Tests / test (3.9) (push) Successful in 1m34s
2024-01-21 21:10:14 +02:00
8b133c8af1 Update dependency isort to v5.13.2
All checks were successful
Tests / test (3.10) (pull_request) Successful in 1m24s
Tests / test (3.11) (pull_request) Successful in 1m24s
Tests / test (3.8) (pull_request) Successful in 1m23s
Tests / test (3.9) (pull_request) Successful in 1m22s
Tests / test (3.10) (push) Successful in 1m36s
Tests / test (3.11) (push) Successful in 1m30s
Tests / test (3.8) (push) Successful in 1m23s
Tests / test (3.9) (push) Successful in 1m25s
2024-01-21 20:06:51 +02:00
b64619d22e Update dependency black to v23.12.1
All checks were successful
Tests / test (3.10) (pull_request) Successful in 1m32s
Tests / test (3.11) (pull_request) Successful in 1m25s
Tests / test (3.8) (pull_request) Successful in 1m23s
Tests / test (3.9) (pull_request) Successful in 1m25s
Tests / test (3.10) (push) Successful in 1m35s
Tests / test (3.11) (push) Successful in 1m27s
Tests / test (3.8) (push) Successful in 1m25s
Tests / test (3.9) (push) Successful in 1m25s
2024-01-21 19:03:40 +02:00
8f222f1cb8 Merge pull request 'Update dependency pylint to v2.17.7' (#1) from renovate/pylint-2.x into master
All checks were successful
Tests / test (3.10) (push) Successful in 1m23s
Tests / test (3.11) (push) Successful in 1m22s
Tests / test (3.8) (push) Successful in 1m23s
Tests / test (3.9) (push) Successful in 1m23s
Reviewed-on: #1
2024-01-21 18:31:05 +02:00
0a395afd24 Merge pull request 'Update dependency pytest to v7.4.4' (#2) from renovate/pytest-7.x into master
Some checks failed
Tests / test (3.11) (push) Waiting to run
Tests / test (3.8) (push) Waiting to run
Tests / test (3.9) (push) Waiting to run
Tests / test (3.10) (push) Has been cancelled
Reviewed-on: #2
2024-01-21 18:30:57 +02:00
e7a098bdf5 RGB mode fixed
Some checks failed
Tests / test (3.8) (push) Waiting to run
Tests / test (3.9) (push) Waiting to run
Tests / test (3.10) (push) Successful in 1m26s
Tests / test (3.11) (push) Has been cancelled
2024-01-21 17:28:57 +01:00
b67b23036d Image mode won't be attempted to be set
Some checks failed
Tests / test (3.10) (push) Failing after 1m23s
Tests / test (3.11) (push) Failing after 1m26s
Tests / test (3.8) (push) Failing after 1m22s
Tests / test (3.9) (push) Failing after 1m23s
2024-01-21 17:05:48 +01:00
d5f4bd79be Update dependency pytest to v7.4.4
Some checks failed
Tests / test (3.11) (pull_request) Has been cancelled
Tests / test (3.8) (pull_request) Has been cancelled
Tests / test (3.9) (pull_request) Has been cancelled
Tests / test (3.10) (pull_request) Has been cancelled
2024-01-21 18:00:43 +02:00
9aed62189d Update dependency pylint to v2.17.7
Some checks failed
Tests / test (3.10) (pull_request) Failing after 1m28s
Tests / test (3.11) (pull_request) Failing after 1m24s
Tests / test (3.8) (pull_request) Failing after 1m24s
Tests / test (3.9) (pull_request) Failing after 1m23s
2024-01-21 18:00:38 +02:00
67b5be094c Add .renovaterc
Some checks failed
Tests / test (3.10) (push) Failing after 1m21s
Tests / test (3.11) (push) Failing after 1m24s
Tests / test (3.8) (push) Failing after 1m21s
Tests / test (3.9) (push) Failing after 1m23s
2024-01-21 17:34:57 +02:00
7afb325ebd Update src/huepaper/__init__.py
Some checks failed
Tests / test (3.10) (push) Failing after 1m23s
Tests / test (3.11) (push) Failing after 1m25s
Tests / test (3.8) (push) Failing after 1m25s
Tests / test (3.9) (push) Has been cancelled
2024-01-21 17:30:36 +02:00
95ed004805 Update requirements/_.txt
Some checks failed
Tests / test (3.11) (push) Waiting to run
Tests / test (3.8) (push) Waiting to run
Tests / test (3.9) (push) Waiting to run
Tests / test (3.10) (push) Has been cancelled
2024-01-21 17:30:16 +02:00
70d0279cb9 Fixed -p and improved coverage
All checks were successful
Tests / test (3.10) (push) Successful in 1m10s
Tests / test (3.11) (push) Successful in 1m9s
Tests / test (3.8) (push) Successful in 1m13s
Tests / test (3.9) (push) Successful in 1m12s
2023-08-10 14:31:33 +02:00
62ef828e4b Removed unused test 2023-08-10 14:22:10 +02:00
ce752d30e2 Added VSCode tasks
All checks were successful
Tests / test (3.10) (push) Successful in 1m11s
Tests / test (3.11) (push) Successful in 1m11s
Tests / test (3.8) (push) Successful in 1m27s
Tests / test (3.9) (push) Successful in 1m21s
2023-08-10 14:20:08 +02:00
1365273ff0 Improved tests 2023-08-10 14:19:56 +02:00
2f4f61ef82 Changed branch name to master
All checks were successful
Tests / test (3.10) (push) Successful in 1m17s
Tests / test (3.11) (push) Successful in 1m24s
Tests / test (3.8) (push) Successful in 1m8s
Tests / test (3.9) (push) Successful in 1m10s
2023-08-10 14:09:51 +02:00
7df479c59e Improved README 2023-08-10 14:09:17 +02:00
13 changed files with 189 additions and 107 deletions

View File

@@ -4,7 +4,7 @@ on:
push: push:
branches: branches:
- dev - dev
- main - master
tags-ignore: tags-ignore:
- v* - v*
pull_request: pull_request:

24
.renovaterc Normal file
View File

@@ -0,0 +1,24 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"pip_requirements": {
"fileMatch": [
"requirements/.*\\.txt$"
],
"enabled": true
},
"packageRules": [
{
"matchUpdateTypes": [
"minor",
"patch",
"pin",
"digest"
],
"automerge": true
}
]
}

View File

@@ -10,5 +10,7 @@
}, },
"[toml]": { "[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml" "editor.defaultFormatter": "tamasfe.even-better-toml"
} },
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true
} }

33
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"linux": {
"command": "./.venv/bin/python -m build"
},
"windows": {
"command": ".\\.venv\\Scripts\\python.exe -m build"
},
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Publish",
"type": "shell",
"linux": {
"command": "./.venv/bin/python -m twine upload --repository gitea ./dist/*"
},
"windows": {
"command": ".\\.venv\\Scripts\\python.exe -m twine upload --repository gitea ./dist/*"
},
"problemMatcher": []
}
]
}

View File

@@ -11,57 +11,23 @@ Mastodon [huebot](https://botsin.space/@huebot).
## Installation ## Installation
### Nix ```shell
pip install --index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple/ huepaper
This project is a [Nix Flake](https://nixos.wiki/wiki/Flakes). If you
have a recent version of the [Nix package manager](https://nixos.org/)
installed and Flakes are enabled, run huepaper like this:
``` example
nix run github:Deleh/huepaper
```
Parameters can be passed by appending a double-dash:
``` example
nix run github:Deleh/huepaper -- -hue 0.5 --color lightblue
```
Global installation can be done by including this flake in your flaked
NixOS configuration as always :)
### Legacy
Execute the following steps to run huepaper:
``` example
pip install -r requirements.txt
./huepaper.py
```
```{=org}
#+end_example
```
To install it in your Python environment run:
``` example
python setup.py install
``` ```
## Usage ## Usage
``` example ```example
usage: huepaper [-h] [-s SIZE] [-c COLOR] [-np] [-o OUTPUT] [-l [LINES]] [-lb [LINES_BRIGHT]] [-ld [LINES_DARK]] [-P [PIXELATE]] [-e EMBLEM] [-hue HUE] [-smin SMIN] [-smax SMAX] [-lmin LMIN] [-lmax LMAX] usage: huepaper [-h] [--width WIDTH] [--height HEIGHT] [-c COLOR] [-np] [-o OUTPUT] [-l [LINES]] [-lb [LINES_BRIGHT]] [-ld [LINES_DARK]] [-p [PIXELATE]] [-e EMBLEM] [-hue HUE] [-smin SMIN] [-smax SMAX] [-lmin LMIN] [-lmax LMAX]
Create wallpapers based on color hues. Create wallpapers based on color hues.
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-s SIZE, --size SIZE size of huepaper in the form WIDTHxHEIGHT (default: 1920x1080) --width WIDTH width of the image (default: 1920)
--height HEIGHT height of the image (default: 1080)
-c COLOR, --color COLOR -c COLOR, --color COLOR
base color from which the huepaper is generated (default: random color) base color from which the huepaper is generated (default: random color)
-np, --no-preview don't preview the huepaper
-o OUTPUT, --output OUTPUT -o OUTPUT, --output OUTPUT
filepath where the huepaper will be saved filepath where the huepaper will be saved
-l [LINES], --lines [LINES] -l [LINES], --lines [LINES]
@@ -81,6 +47,14 @@ optional arguments:
-lmax LMAX maximum luminance for colors in range [0, 1] (default: 0.9) -lmax LMAX maximum luminance for colors in range [0, 1] (default: 0.9)
``` ```
...or as a Python module
```python
from huepaper import generate
image = generate(width=500, height=500, hue_max=1.0, lum_min=0.3, lum_max=0.6, sat_min=0.8, sat_max=1.0)
```
All image operations are called in order of the help file. E.g. pixelate All image operations are called in order of the help file. E.g. pixelate
(`-p`) is called after adding lines (`-l`). (`-p`) is called after adding lines (`-l`).
@@ -141,7 +115,7 @@ huepaper -hue 1.0 -lmin 0.3 -lmax 0.6 -smin 0.8 -smax 1.0
![Huepaper 5](images/huepaper_5.png) ![Huepaper 5](images/huepaper_5.png)
``` example ``` example
huepaper -hue 0.3 -lmin 0.5 -lmax 0.5 -l 0.5 -P 64x36 huepaper -hue 0.3 -lmin 0.5 -lmax 0.5 -l 0.5 -p 64x36
``` ```
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@@ -1,3 +1,3 @@
colour==0.1.5 colour==0.1.5
numpy~=1.24.0,<1.25.0 numpy~=1.24.0,<1.25.0
pillow~=10.0.0 pillow~=10.2.0

View File

@@ -1,9 +1,9 @@
black==23.7.0 black==23.12.1
build==0.10.0 build==1.0.3
isort==5.12.0 isort==5.13.2
mypy==1.4.1 mypy==1.8.0
pylint==2.17.5 pylint==2.17.7
pytest-cov==4.1.0 pytest-cov==4.1.0
pytest==7.4.0 pytest==7.4.4
tox==4.7.0 tox==4.7.0
twine==4.0.2 twine==4.0.2

View File

@@ -1,4 +1,4 @@
__version__ = "0.0.4" __version__ = "0.0.5"
from . import utils from . import utils
from .generator import generate from .generator import generate

View File

@@ -44,10 +44,10 @@ def generate(
raise ValueError("Pixelation value must be set in form: 42x42") raise ValueError("Pixelation value must be set in form: 42x42")
try: try:
random_color = False if color else True random_color = not color
base_color = get_base_color(color, sat_min, sat_max, lum_min, lum_max) base_color = get_base_color(color, sat_min, sat_max, lum_min, lum_max)
if random_color: if random_color:
print("Selected random base color: {}".format(base_color.hex)) print(f"Selected random base color: {base_color.hex}")
c1, c2, c3, c4 = create_colors( c1, c2, c3, c4 = create_colors(
base_color, hue_max, sat_min, sat_max, lum_min, lum_max base_color, hue_max, sat_min, sat_max, lum_min, lum_max
@@ -68,7 +68,7 @@ def generate(
if emblem: if emblem:
image = add_emblem(image, emblem) image = add_emblem(image, emblem)
image.mode = "RGB" image = image.convert(mode="RGB")
if _output: if _output:
save_image(image, _output) save_image(image, _output)
@@ -77,5 +77,5 @@ def generate(
return image return image
except Exception as e: except Exception as e:
print(str(e)) print(e)
exit(1) exit(1)

View File

@@ -4,6 +4,7 @@ import random
import numpy as np import numpy as np
from colour import Color from colour import Color
from PIL import Image, ImageDraw, ImageOps from PIL import Image, ImageDraw, ImageOps
from typing import Tuple, Union
def get_base_color( def get_base_color(
@@ -23,9 +24,9 @@ def get_base_color(
base_color = Color(color_string) base_color = Color(color_string)
except: except:
try: try:
base_color = Color("#{}".format(color_string)) base_color = Color(f"#{color_string}")
except: except:
raise Exception("Invalid color expression: {}".format(color_string)) raise Exception(f"Invalid color expression: {color_string}")
return base_color return base_color
@@ -43,7 +44,7 @@ def create_colors(
max_lum_diff = 0.1 max_lum_diff = 0.1
# Create four random colors similar to the given base_color # Create four random colors similar to the given base_color
for i in range(0, 4): for _ in range(4):
tmp_hue = base_color.hue + random.uniform(-hue_max / 2.0, hue_max / 2.0) tmp_hue = base_color.hue + random.uniform(-hue_max / 2.0, hue_max / 2.0)
if tmp_hue > 1.0: if tmp_hue > 1.0:
tmp_hue -= 1 tmp_hue -= 1
@@ -60,7 +61,7 @@ def create_colors(
return tuple(colors) return tuple(colors)
def create_base_image(c1, c2, c3, c4, width=1920, height=1080): def create_base_image(c1, c2, c3, c4, width=1920, height=1080) -> Image:
"""Create a base huepaper by four corner colors. """Create a base huepaper by four corner colors.
c1 - top left c1 - top left
@@ -79,12 +80,11 @@ def create_base_image(c1, c2, c3, c4, width=1920, height=1080):
) )
im_arr = np.array([r, g, b]).T im_arr = np.array([r, g, b]).T
image = Image.fromarray(np.uint8(im_arr * 255)).convert("RGBA")
return Image.fromarray(np.uint8(im_arr * 255)).convert("RGBA")
return image
def add_lines(image, color): def add_lines(image: Image, color: Tuple[float, float, float, Union[float, None]]) -> Image:
"""Add one to three random lines to an image with given color.""" """Add one to three random lines to an image with given color."""
width, height = image.size width, height = image.size
@@ -103,7 +103,8 @@ def add_lines(image, color):
) )
space = rand_width() // 2 space = rand_width() // 2
offset = random.randint(0, space) offset = random.randint(0, space)
for i in range(0, number_of_lines):
for _ in range(number_of_lines):
line_width = rand_width() line_width = rand_width()
x = offset + space + (line_width // 2) x = offset + space + (line_width // 2)
draw.line((x, 0, x, height), fill=color, width=line_width) draw.line((x, 0, x, height), fill=color, width=line_width)
@@ -138,7 +139,7 @@ def add_emblem(image, filepath):
try: try:
emblem_image = Image.open(filepath) emblem_image = Image.open(filepath)
except Exception as e: except Exception as e:
raise Exception("Failed to load emblem: {}".format(e)) raise Exception(f"Failed to load emblem: {e}")
# Exit if emblem is too big # Exit if emblem is too big
if emblem_image.size[0] > width or emblem_image.size[1] > height: if emblem_image.size[0] > width or emblem_image.size[1] > height:
@@ -161,11 +162,10 @@ def save_image(image, filepath):
# Check whether file exists # Check whether file exists
if os.path.isfile(filepath): if os.path.isfile(filepath):
overwrite = input( overwrite = input(
"The file {} already exists. Do you want to overwrite it? [y/N] ".format( f"The file {filepath} already exists. Do you want to overwrite it? [y/N] "
filepath
)
) )
if overwrite != "y" and overwrite != "Y":
if overwrite not in ["y", "Y"]:
save = False save = False
if save: if save:
@@ -175,9 +175,10 @@ def save_image(image, filepath):
image.save(filepath) image.save(filepath)
stop = True stop = True
except Exception as e: except Exception as e:
print("Failed to save wallpaper: {}".format(e)) print(f"Failed to save wallpaper: {e}")
again = input("Do you want to try again? [Y/n] ") again = input("Do you want to try again? [Y/n] ")
if again == "n" or again == "N":
if again in ["n", "N"]:
stop = True stop = True
else: else:
filepath = input( filepath = input(

BIN
tests/assets/emblem.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -1,36 +0,0 @@
from os import remove
from pathlib import Path
from PIL.Image import Image
from huepaper import generate
from huepaper.utils import save_image
def test_generation():
image = generate(
width=500,
height=500,
hue_max=1.0,
lum_min=0.3,
lum_max=0.6,
sat_min=0.8,
sat_max=1.0,
)
assert isinstance(image, Image)
def test_saving():
image = generate(
500,
500,
hue_max=1.0,
lum_min=0.3,
lum_max=0.6,
sat_min=0.8,
sat_max=1.0,
lines=0.0,
)
save_image(image, Path("tests/image.jpg"))
assert Path("tests/image.jpg").exists()
remove(Path("tests/image.jpg"))

84
tests/test_generator.py Normal file
View File

@@ -0,0 +1,84 @@
from os import remove
from pathlib import Path
from subprocess import run
from PIL.Image import Image
from huepaper import generate
from huepaper.utils import save_image
def test_generation():
image = generate()
assert isinstance(image, Image)
def test_saving():
image = generate(
500,
500,
hue_max=1.0,
lum_min=0.3,
lum_max=0.6,
sat_min=0.8,
sat_max=1.0,
lines=0.0,
)
save_image(image, Path("tests/image.jpg"))
assert Path("tests/image.jpg").exists()
remove(Path("tests/image.jpg"))
def test_shell():
assert (
run(
[
"huepaper",
"-hue",
"0.3",
"-lmin",
"0.5",
"-lmax",
"0.3",
"-l",
"0.5",
"-p",
"64x36",
"-o",
"tests/image.jpg",
],
check=False,
).returncode
== 0
)
remove(Path("tests/image.jpg"))
def test_generation_example_1():
image = generate(color="lightgreen")
assert isinstance(image, Image)
def test_generation_example_2():
image = generate(color="#ff7f50", lines_bright=0.05)
assert isinstance(image, Image)
def test_generation_example_3():
image = generate(hue_max=1.0, lum_min=0.3, lum_max=0.6, sat_min=0.8, sat_max=1.0)
assert isinstance(image, Image)
def test_generation_example_4():
image = generate(hue_max=0.3, lum_min=0.5, lum_max=0.5, lines=0.5, pixelate="64x36")
assert isinstance(image, Image)
def test_generation_example_5():
image = generate(
lines=0.3,
lines_bright=0.1,
lines_dark=0.1,
emblem=Path("tests/assets/emblem.png"),
)
assert isinstance(image, Image)