222 Commits

15 changed files with 210 additions and 125 deletions
+5 -4
View File
@@ -4,18 +4,17 @@ on:
push:
branches:
- dev
- main
- master
tags-ignore:
- v*
pull_request:
jobs:
test:
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
@@ -23,6 +22,8 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: './requirements/*'
env:
AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache
- name: Install dependencies
+27
View File
@@ -0,0 +1,27 @@
{
"$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
}
],
"ignoreDeps": [
"numpy"
]
}
+3 -1
View File
@@ -10,5 +10,7 @@
},
"[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml"
}
},
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true
}
+33
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": []
}
]
}
+15 -41
View File
@@ -11,57 +11,23 @@ Mastodon [huebot](https://botsin.space/@huebot).
## Installation
### Nix
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
```shell
pip install --index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple/ huepaper
```
## Usage
``` 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]
```example
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.
optional arguments:
-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
base color from which the huepaper is generated (default: random color)
-np, --no-preview don't preview the huepaper
-o OUTPUT, --output OUTPUT
filepath where the huepaper will be saved
-l [LINES], --lines [LINES]
@@ -81,6 +47,14 @@ optional arguments:
-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
(`-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)
``` 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
```
------------------------------------------------------------------------
+5 -6
View File
@@ -9,7 +9,7 @@ authors = [{ name = "Denis Lehmann" }]
maintainers = [{ name = "Profitroll" }]
description = "A colorful wallpaper generator"
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.10"
license = { text = "GPL3" }
classifiers = [
"Development Status :: 3 - Alpha",
@@ -17,11 +17,10 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Utilities",
]
@@ -44,7 +43,7 @@ dev = { file = "requirements/dev.txt" }
where = ["src"]
[tool.black]
target-version = ['py38', 'py39', 'py310', 'py311']
target-version = ['py310', 'py311', 'py312', 'py313']
[tool.isort]
profile = "black"
@@ -62,7 +61,7 @@ strict = true
show_error_codes = true
[tool.pylint.main]
py-version = 3.8
py-version = 3.10
[tool.coverage.run]
source = ["huepaper"]
+2 -2
View File
@@ -1,3 +1,3 @@
colour==0.1.5
numpy~=1.24.0,<1.25.0
pillow~=10.0.0
numpy~=2.0.1,<2.1.0
pillow~=11.3.0
+9 -9
View File
@@ -1,9 +1,9 @@
black==23.7.0
build==0.10.0
isort==5.12.0
mypy==1.4.1
pylint==2.17.5
pytest-cov==4.1.0
pytest==7.4.0
tox==4.7.0
twine==4.0.2
black==26.1.0
build==1.4.0
isort==5.13.2
mypy==1.19.1
pylint==3.3.9
pytest-cov==7.0.0
pytest==9.0.2
tox==4.47.3
twine==6.2.0
+1 -1
View File
@@ -1,4 +1,4 @@
__version__ = "0.0.4"
__version__ = "0.0.5"
from . import utils
from .generator import generate
+4 -4
View File
@@ -44,10 +44,10 @@ def generate(
raise ValueError("Pixelation value must be set in form: 42x42")
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)
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(
base_color, hue_max, sat_min, sat_max, lum_min, lum_max
@@ -68,7 +68,7 @@ def generate(
if emblem:
image = add_emblem(image, emblem)
image.mode = "RGB"
image = image.convert(mode="RGB")
if _output:
save_image(image, _output)
@@ -77,5 +77,5 @@ def generate(
return image
except Exception as e:
print(str(e))
print(e)
exit(1)
+17 -16
View File
@@ -4,6 +4,7 @@ import random
import numpy as np
from colour import Color
from PIL import Image, ImageDraw, ImageOps
from typing import Tuple, Union
def get_base_color(
@@ -23,9 +24,9 @@ def get_base_color(
base_color = Color(color_string)
except:
try:
base_color = Color("#{}".format(color_string))
base_color = Color(f"#{color_string}")
except:
raise Exception("Invalid color expression: {}".format(color_string))
raise Exception(f"Invalid color expression: {color_string}")
return base_color
@@ -43,7 +44,7 @@ def create_colors(
max_lum_diff = 0.1
# 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)
if tmp_hue > 1.0:
tmp_hue -= 1
@@ -60,7 +61,7 @@ def create_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.
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
image = Image.fromarray(np.uint8(im_arr * 255)).convert("RGBA")
return image
return Image.fromarray(np.uint8(im_arr * 255)).convert("RGBA")
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."""
width, height = image.size
@@ -103,7 +103,8 @@ def add_lines(image, color):
)
space = rand_width() // 2
offset = random.randint(0, space)
for i in range(0, number_of_lines):
for _ in range(number_of_lines):
line_width = rand_width()
x = offset + space + (line_width // 2)
draw.line((x, 0, x, height), fill=color, width=line_width)
@@ -138,7 +139,7 @@ def add_emblem(image, filepath):
try:
emblem_image = Image.open(filepath)
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
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
if os.path.isfile(filepath):
overwrite = input(
"The file {} already exists. Do you want to overwrite it? [y/N] ".format(
filepath
)
f"The file {filepath} already exists. Do you want to overwrite it? [y/N] "
)
if overwrite != "y" and overwrite != "Y":
if overwrite not in ["y", "Y"]:
save = False
if save:
@@ -175,9 +175,10 @@ def save_image(image, filepath):
image.save(filepath)
stop = True
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] ")
if again == "n" or again == "N":
if again in ["n", "N"]:
stop = True
else:
filepath = input(
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

-36
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
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)
+5 -5
View File
@@ -1,14 +1,14 @@
[tox]
minversion = 3.8.0
envlist = py38, py39, py310, py311
minversion = 3.10.0
envlist = py310, py311, py312, py313
isolated_build = true
[gh-actions]
python =
3.8: py38
3.9: py39
python =
3.10: py310
3.11: py311
3.12: py312
3.13: py313
[testenv]
setenv =