Last active
September 20, 2018 06:55
-
-
Save xunil154/5bcd2ae80cb9e87dd0a880c98d60765d to your computer and use it in GitHub Desktop.
Wallpaper chooser script. Looks for wallpapers that match your current X11 aspect ratio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2.7 | |
""" | |
Dependencies: | |
* Python Image Library (PIL) | |
* Xlib | |
* feh (system command) | |
This script crawls recursivly through the directory provided as the first | |
argument and looks for any files matching the extensions: jpg, png, jpeg, tiff | |
The aspect ratio of each image is then calculated | |
The current aspect ratio of the root X11 window is then computed | |
A search is then ran looking for images that match the current aspect ratio | |
within a 20% error | |
A wallpaper is then randomly selected and applied | |
https://github.com/xunil154 | |
""" | |
import sys | |
import subprocess | |
import os | |
import random | |
import bisect | |
try: | |
from PIL import Image | |
except: | |
print("Python library 'PIL' not found - must be present to work") | |
sys.exit(1) | |
try: | |
from Xlib import display | |
except: | |
print("Python library 'Xlib' not found - must be present to work") | |
sys.exit(1) | |
if os.system('which feh > /dev/null') != 0: | |
print("System command 'feh' not found - must be present to work") | |
sys.exit(1) | |
EXTENSIONS = ["jpg", "png", "jpeg", "tiff"] | |
WPs = [] | |
_processed = set() | |
FIT_THRESHOLD = 5.5 # Ignore any wallpapers below this threshold | |
class WP(object): | |
image = None | |
path = None | |
aspect = 0.0000001 | |
resolution = 0 | |
fit = 0 | |
def __init__(self, path): | |
self.path = path | |
self.image = Image.open(self.path) | |
size = self.image.size | |
self.resolution = size[0] * size[1] | |
self.aspect = float(max(size)) / float(min(size)) # aspect ratio | |
# returns abs match, 0 means perfect match | |
def fit(self, t_width, t_height): | |
# Aspect plays a more important role than resolution | |
# t_aspect will always be >= 1 | |
t_aspect = float(max(t_width, t_height)) / float(min(t_width,t_height)) | |
fit_aspect = abs(t_aspect - self.aspect) | |
fit_aspect_r = 1 - (1 / float(max(t_aspect, self.aspect)) / float(min(t_aspect,self.aspect))) | |
self.fit = fit_aspect + 5 # Add one to leave room for resolution tuning | |
# Resolution is used to reduce that extra 1 added above | |
# This is only done if our image resolution is greater than the target | |
# It also takes into account how close it is to the aspect ratio | |
# e.g a high res 16:9 tripple monitor should not modify the fit at all | |
# for a single 16:9 monitor because the aspect ratio is so different | |
t_resolution = t_width * t_height | |
if t_resolution < self.resolution: | |
fit_res = 5 - ((t_resolution - self.resolution) / float(t_resolution)) | |
fit_res_mod = 1 / (fit_res / (fit_aspect + 0.00000001)) | |
self.fit = self.fit - fit_res_mod | |
return self.fit | |
def register_wp(image): | |
if image in _processed: | |
return | |
wp = WP(image) | |
WPs.append(wp) | |
_processed.add(image) | |
def set_wp(image): | |
cmd=["/usr/bin/feh","--bg-center","--no-xinerama", image] | |
cmd=["/usr/bin/feh","--bg-max","--no-xinerama", image] | |
cmd=["/usr/bin/feh","--bg-fill","--no-xinerama", image] | |
subprocess.Popen(cmd) | |
def get_screen_size(): | |
d = display.Display() | |
s = d.screen() | |
w = s.width_in_pixels | |
h = s.height_in_pixels | |
return w, h | |
def get_possible_wallpapers(): | |
t_w, t_h = get_screen_size() | |
possible = [x for x in WPs if x.fit(t_w, t_h) <= FIT_THRESHOLD] | |
possible.sort(key=lambda x: -1 * x.fit) | |
#for wp in possible: | |
# print("WP: %s:\t%f\t%dx%d\t%d\t%d" % (wp.path, wp.fit, wp.image.size[0], wp.image.size[1], wp.resolution, t_w * t_h)) | |
return possible | |
# Random weighted choice | |
def pick_wp(choices): | |
# Normalize | |
# [ 0.2, 0.3, 1, 2, 4 ] | |
# [ 1/0.2, 1/0.3, 1/1, 1/2, 1/4 ] | |
# [ 5, 3.3, 1, 0.5, 0.25 ] | |
norm = [1-(1/x.fit) for x in choices] | |
norm_max = max(norm) | |
norm = [x * 1/norm_max for x in norm] | |
running_total = sum(norm) | |
rnd = random.random() * running_total | |
for i, w in enumerate(norm): | |
rnd -= w | |
if rnd < 0: | |
#print("Picked position %d, weight %f, fit %f" %( i, w, choices[i].fit)) | |
#print(choices[i].path) | |
return choices[i] | |
return None | |
def process_dir(path): | |
wallpapers = [] | |
for root, dirs, files in os.walk(basedir): | |
for f in files: | |
for e in EXTENSIONS: | |
if e in f.lower(): | |
wallpapers.append(os.path.join(root, f)) | |
for wp in wallpapers: | |
register_wp(wp) | |
if __name__ == "__main__": | |
if len(sys.argv) != 2: | |
print("Usage: %s <path>"% sys.argv[0]) | |
sys.exit(1) | |
basedir = sys.argv[1] | |
process_dir(basedir) | |
available = get_possible_wallpapers() # look within 20% | |
# Pick using an inverse log function to priortize the higher matches | |
wp = pick_wp(available) | |
set_wp(wp.path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment