From fd176b77f01e67307c9899735ecac01939b2aebf Mon Sep 17 00:00:00 2001 From: James Bunton Date: Tue, 11 Nov 2014 19:47:47 +1100 Subject: [PATCH 1/1] tabs -> spaces --- asyncsched.py | 80 ++--- randombg.py | 790 ++++++++++++++++++++++++------------------------- wallchanger.py | 476 ++++++++++++++--------------- 3 files changed, 673 insertions(+), 673 deletions(-) diff --git a/asyncsched.py b/asyncsched.py index cae8713..22eb788 100644 --- a/asyncsched.py +++ b/asyncsched.py @@ -11,58 +11,58 @@ tasks = [] running = False class Task(object): - def __init__(self, delay, func, args=[], kwargs={}): - self.time = time.time() + delay - self.func = lambda: func(*args, **kwargs) - - def __cmp__(self, other): - return cmp(self.time, other.time) + def __init__(self, delay, func, args=[], kwargs={}): + self.time = time.time() + delay + self.func = lambda: func(*args, **kwargs) + + def __cmp__(self, other): + return cmp(self.time, other.time) - def __call__(self): - f = self.func - self.func = None - if f: - return f() + def __call__(self): + f = self.func + self.func = None + if f: + return f() - def cancel(self): - assert self.func is not None - self.func = None + def cancel(self): + assert self.func is not None + self.func = None def schedule(delay, func, args=[], kwargs={}): - task = Task(delay, func, args, kwargs) - heapq.heappush(tasks, task) - return task + task = Task(delay, func, args, kwargs) + heapq.heappush(tasks, task) + return task def loop(timeout=30.0, use_poll=False): - global running - running = True - oldhandler = signal.signal(signal.SIGTERM, exit) + global running + running = True + oldhandler = signal.signal(signal.SIGTERM, exit) - if use_poll: - if hasattr(select, 'poll'): - poll_fun = asyncore.poll3 - else: - poll_fun = asyncore.poll2 - else: - poll_fun = asyncore.poll + if use_poll: + if hasattr(select, 'poll'): + poll_fun = asyncore.poll3 + else: + poll_fun = asyncore.poll2 + else: + poll_fun = asyncore.poll - while running: - now = time.time() - while tasks and tasks[0].time < now: - task = heapq.heappop(tasks) - task() + while running: + now = time.time() + while tasks and tasks[0].time < now: + task = heapq.heappop(tasks) + task() - t = timeout - if tasks: - t = max(min(t, tasks[0].time - now), 0) + t = timeout + if tasks: + t = max(min(t, tasks[0].time - now), 0) - poll_fun(timeout=t) - - signal.signal(signal.SIGTERM, oldhandler) + poll_fun(timeout=t) + + signal.signal(signal.SIGTERM, oldhandler) def exit(*args): - global running - running = False + global running + running = False __all__ = ("schedule", "loop", "exit") diff --git a/randombg.py b/randombg.py index 51f6f72..63e9460 100755 --- a/randombg.py +++ b/randombg.py @@ -13,463 +13,463 @@ import time from optparse import OptionParser import logging try: - logging.basicConfig(format="%(levelname)s: %(message)s") + logging.basicConfig(format="%(levelname)s: %(message)s") except TypeError: # Python 2.3's logging.basicConfig does not support parameters - logging.basicConfig() + logging.basicConfig() try: - import cPickle as pickle + import cPickle as pickle except ImportError: - import pickle + import pickle try: - # Required libraries - import asyncsched - import wallchanger + # Required libraries + import asyncsched + import wallchanger except ImportError, e: - logging.critical("Missing libraries! Exiting...", exc_info=1) - sys.exit(1) + logging.critical("Missing libraries! Exiting...", exc_info=1) + sys.exit(1) def filter_images(filenames): - extensions = ('.jpg', '.jpe', '.jpeg', '.png', '.gif', '.bmp') - for filename in filenames: - _, ext = os.path.splitext(filename) - if ext.lower() in extensions: - yield filename + extensions = ('.jpg', '.jpe', '.jpeg', '.png', '.gif', '.bmp') + for filename in filenames: + _, ext = os.path.splitext(filename) + if ext.lower() in extensions: + yield filename class BaseFileList(object): - """Base file list implementation""" - def __init__(self): - self.paths = [] - self.favourites = [] + """Base file list implementation""" + def __init__(self): + self.paths = [] + self.favourites = [] - def add_path(self, path): - self.paths.append(path) + def add_path(self, path): + self.paths.append(path) - def store_cache(self, filename): - try: - logging.debug("Attempting to store cache") - fd = open(filename, 'wb') - pickle.dump(self, fd, 2) - logging.debug("Cache successfully stored") - except Exception, e: - warning("Storing cache: %s" % e) + def store_cache(self, filename): + try: + logging.debug("Attempting to store cache") + fd = open(filename, 'wb') + pickle.dump(self, fd, 2) + logging.debug("Cache successfully stored") + except Exception, e: + warning("Storing cache: %s" % e) - def load_cache(self, filename): - try: - logging.debug("Attempting to load cache from: %s" % filename) - self.paths.sort() + def load_cache(self, filename): + try: + logging.debug("Attempting to load cache from: %s" % filename) + self.paths.sort() - fd = open(filename, 'rb') - tmp = pickle.load(fd) + fd = open(filename, 'rb') + tmp = pickle.load(fd) - if tmp.__class__ != self.__class__: - raise ValueError("Using different file list type") + if tmp.__class__ != self.__class__: + raise ValueError("Using different file list type") - tmp.paths.sort() - if self.paths != tmp.paths: - raise ValueError, "Path list changed" + tmp.paths.sort() + if self.paths != tmp.paths: + raise ValueError, "Path list changed" - # Overwrite this object with the other - for attr, value in tmp.__dict__.items(): - setattr(self, attr, value) + # Overwrite this object with the other + for attr, value in tmp.__dict__.items(): + setattr(self, attr, value) - return True + return True - except Exception, e: - logging.warning("Loading cache: %s" % e) - return False + except Exception, e: + logging.warning("Loading cache: %s" % e) + return False - def add_to_favourites(self): - '''Adds the current image to the list of favourites''' - self.favourites.append(self.get_current_image()) + def add_to_favourites(self): + '''Adds the current image to the list of favourites''' + self.favourites.append(self.get_current_image()) - def scan_paths(self): - raise NotImplementedError() + def scan_paths(self): + raise NotImplementedError() - def get_next_image(self): - raise NotImplementedError() + def get_next_image(self): + raise NotImplementedError() - def get_prev_image(self): - raise NotImplementedError() + def get_prev_image(self): + raise NotImplementedError() - def get_current_image(self): - raise NotImplementedError() + def get_current_image(self): + raise NotImplementedError() - def is_empty(self): - return True + def is_empty(self): + return True class RandomFileList(BaseFileList): - def __init__(self): - super(RandomFileList, self).__init__() - self.list = [] - self.last_image = None - - def scan_paths(self): - for path in self.paths: - for dirpath, dirsnames, filenames in os.walk(path): - for filename in filter_images(filenames): - self.list.append(os.path.join(dirpath, filename)) - - def add_path(self, path): - self.paths.append(path) - logging.debug('Added path "%s" to the list' % path) - - def get_next_image(self): - n = random.randint(0, len(self.list)-1) - self.last_image = self.list[n] - logging.debug("Picked file '%s' from list" % self.last_image) - return self.last_image - - def get_current_image(self): - if self.last_image: - return self.last_image - else: - return self.get_next_image() - - def is_empty(self): - return len(self.list) == 0 + def __init__(self): + super(RandomFileList, self).__init__() + self.list = [] + self.last_image = None + + def scan_paths(self): + for path in self.paths: + for dirpath, dirsnames, filenames in os.walk(path): + for filename in filter_images(filenames): + self.list.append(os.path.join(dirpath, filename)) + + def add_path(self, path): + self.paths.append(path) + logging.debug('Added path "%s" to the list' % path) + + def get_next_image(self): + n = random.randint(0, len(self.list)-1) + self.last_image = self.list[n] + logging.debug("Picked file '%s' from list" % self.last_image) + return self.last_image + + def get_current_image(self): + if self.last_image: + return self.last_image + else: + return self.get_next_image() + + def is_empty(self): + return len(self.list) == 0 class AllRandomFileList(BaseFileList): - def __init__(self): - super(AllRandomFileList, self).__init__() - self.list = None - self.imagePointer = 0 - - # Scan the input directory, and then randomize the file list - def scan_paths(self): - logging.debug("Scanning paths") - - self.list = [] - for path in self.paths: - logging.debug('Scanning "%s"' % path) - for dirpath, dirsnames, filenames in os.walk(path): - for filename in filter_images(filenames): - logging.debug('Adding file "%s"' % filename) - self.list.append(os.path.join(dirpath, filename)) - - random.shuffle(self.list) - - def add_path(self, path): - self.paths.append(path) - logging.debug('Added path "%s" to the list' % path) - - def store_cache(self, filename): - try: - fd = open(filename, 'wb') - pickle.dump(self, fd, 2) - logging.debug("Cache successfully stored") - except Exception, e: - logging.warning("Storing cache", exc_info=1) - - def get_current_image(self): - return self.list[self.imagePointer] - - def __inc_in_range(self, n, amount = 1, rangeMax = None, rangeMin = 0): - if rangeMax == None: rangeMax = len(self.list) - assert rangeMax > 0 - return (n + amount) % rangeMax - - def get_next_image(self): - self.imagePointer = self.__inc_in_range(self.imagePointer) - imageName = self.list[self.imagePointer] - logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer)) - return imageName - - def get_prev_image(self): - self.imagePointer = self.__inc_in_range(self.imagePointer, amount=-1) - imageName = self.list[self.imagePointer] - logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer)) - return imageName - - def is_empty(self): - return len(self.list) == 0 + def __init__(self): + super(AllRandomFileList, self).__init__() + self.list = None + self.imagePointer = 0 + + # Scan the input directory, and then randomize the file list + def scan_paths(self): + logging.debug("Scanning paths") + + self.list = [] + for path in self.paths: + logging.debug('Scanning "%s"' % path) + for dirpath, dirsnames, filenames in os.walk(path): + for filename in filter_images(filenames): + logging.debug('Adding file "%s"' % filename) + self.list.append(os.path.join(dirpath, filename)) + + random.shuffle(self.list) + + def add_path(self, path): + self.paths.append(path) + logging.debug('Added path "%s" to the list' % path) + + def store_cache(self, filename): + try: + fd = open(filename, 'wb') + pickle.dump(self, fd, 2) + logging.debug("Cache successfully stored") + except Exception, e: + logging.warning("Storing cache", exc_info=1) + + def get_current_image(self): + return self.list[self.imagePointer] + + def __inc_in_range(self, n, amount = 1, rangeMax = None, rangeMin = 0): + if rangeMax == None: rangeMax = len(self.list) + assert rangeMax > 0 + return (n + amount) % rangeMax + + def get_next_image(self): + self.imagePointer = self.__inc_in_range(self.imagePointer) + imageName = self.list[self.imagePointer] + logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer)) + return imageName + + def get_prev_image(self): + self.imagePointer = self.__inc_in_range(self.imagePointer, amount=-1) + imageName = self.list[self.imagePointer] + logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer)) + return imageName + + def is_empty(self): + return len(self.list) == 0 class FolderRandomFileList(BaseFileList): - """A file list that will pick a file randomly within a directory. Each - directory has the same chance of being chosen.""" - def __init__(self): - super(FolderRandomFileList, self).__init__() - self.directories = {} - self.last_image = None - - def scan_paths(self): - pass - - def add_path(self, path): - logging.debug('Added path "%s" to the list' % path) - for dirpath, dirs, filenames in os.walk(path): - logging.debug('Scanning "%s" for images' % dirpath) - if self.directories.has_key(dirpath): - continue - filenames = list(filter_images(filenames)) - if len(filenames): - self.directories[dirpath] = filenames - logging.debug('Adding "%s" to "%s"' % (filenames, dirpath)) - else: - logging.debug("No images found in '%s'" % dirpath) - - def get_next_image(self): - directory = random.choice(self.directories.keys()) - logging.debug('directory: "%s"' % directory) - filename = random.choice(self.directories[directory]) - logging.debug('filename: "%s"' % filename) - return os.path.join(directory, filename) - - def get_current_image(self): - if self.last_image: - return self.last_image - else: - return self.get_next_image() - - def is_empty(self): - return len(self.directories.values()) == 0 + """A file list that will pick a file randomly within a directory. Each + directory has the same chance of being chosen.""" + def __init__(self): + super(FolderRandomFileList, self).__init__() + self.directories = {} + self.last_image = None + + def scan_paths(self): + pass + + def add_path(self, path): + logging.debug('Added path "%s" to the list' % path) + for dirpath, dirs, filenames in os.walk(path): + logging.debug('Scanning "%s" for images' % dirpath) + if self.directories.has_key(dirpath): + continue + filenames = list(filter_images(filenames)) + if len(filenames): + self.directories[dirpath] = filenames + logging.debug('Adding "%s" to "%s"' % (filenames, dirpath)) + else: + logging.debug("No images found in '%s'" % dirpath) + + def get_next_image(self): + directory = random.choice(self.directories.keys()) + logging.debug('directory: "%s"' % directory) + filename = random.choice(self.directories[directory]) + logging.debug('filename: "%s"' % filename) + return os.path.join(directory, filename) + + def get_current_image(self): + if self.last_image: + return self.last_image + else: + return self.get_next_image() + + def is_empty(self): + return len(self.directories.values()) == 0 class Cycler(object): - def init(self, options, paths, oneshot=False): - self.cycle_time = options.cycle_time - self.cache_filename = options.cache_filename - - logging.debug("Initialising wallchanger") - wallchanger.init(options.background_colour, options.convert) - - logging.debug("Initialising file list") - if options.all_random: - self.filelist = AllRandomFileList() - elif options.folder_random: - self.filelist = FolderRandomFileList() - else: - self.filelist = RandomFileList() - - for path in paths: - self.filelist.add_path(path) - - if self.filelist.load_cache(self.cache_filename): - logging.debug("Loaded cache successfully") - else: - logging.debug("Could not load cache") - self.filelist.scan_paths() - - if self.filelist.is_empty(): - logging.error("No images were found. Exiting...") - sys.exit(1) - - self.task = None - if oneshot: - self.cmd_next() - else: - self.cmd_reload() - - def finish(self): - self.filelist.store_cache(self.cache_filename) - - def find_files(self, options, paths): - return filelist - - def cmd_reset(self): - def next(): - image = self.filelist.get_next_image() - wallchanger.set_image(image) - self.task = None - self.cmd_reset() - - if self.task is not None: - self.task.cancel() - self.task = asyncsched.schedule(self.cycle_time, next) - logging.debug("Reset timer for %s seconds" % self.cycle_time) - self.filelist.store_cache(self.cache_filename) - - def cmd_reload(self): - image = self.filelist.get_current_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_next(self): - image = self.filelist.get_next_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_prev(self): - image = self.filelist.get_prev_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_rescan(self): - self.filelist.scan_paths() - - def cmd_pause(self): - if self.task is not None: - self.task.cancel() - self.task = None - - def cmd_exit(self): - asyncsched.exit() - - def cmd_favourite(self): - self.filelist.add_to_favourites() + def init(self, options, paths, oneshot=False): + self.cycle_time = options.cycle_time + self.cache_filename = options.cache_filename + + logging.debug("Initialising wallchanger") + wallchanger.init(options.background_colour, options.convert) + + logging.debug("Initialising file list") + if options.all_random: + self.filelist = AllRandomFileList() + elif options.folder_random: + self.filelist = FolderRandomFileList() + else: + self.filelist = RandomFileList() + + for path in paths: + self.filelist.add_path(path) + + if self.filelist.load_cache(self.cache_filename): + logging.debug("Loaded cache successfully") + else: + logging.debug("Could not load cache") + self.filelist.scan_paths() + + if self.filelist.is_empty(): + logging.error("No images were found. Exiting...") + sys.exit(1) + + self.task = None + if oneshot: + self.cmd_next() + else: + self.cmd_reload() + + def finish(self): + self.filelist.store_cache(self.cache_filename) + + def find_files(self, options, paths): + return filelist + + def cmd_reset(self): + def next(): + image = self.filelist.get_next_image() + wallchanger.set_image(image) + self.task = None + self.cmd_reset() + + if self.task is not None: + self.task.cancel() + self.task = asyncsched.schedule(self.cycle_time, next) + logging.debug("Reset timer for %s seconds" % self.cycle_time) + self.filelist.store_cache(self.cache_filename) + + def cmd_reload(self): + image = self.filelist.get_current_image() + wallchanger.set_image(image) + self.cmd_reset() + + def cmd_next(self): + image = self.filelist.get_next_image() + wallchanger.set_image(image) + self.cmd_reset() + + def cmd_prev(self): + image = self.filelist.get_prev_image() + wallchanger.set_image(image) + self.cmd_reset() + + def cmd_rescan(self): + self.filelist.scan_paths() + + def cmd_pause(self): + if self.task is not None: + self.task.cancel() + self.task = None + + def cmd_exit(self): + asyncsched.exit() + + def cmd_favourite(self): + self.filelist.add_to_favourites() class Server(asynchat.async_chat): - def __init__(self, cycler, sock): - asynchat.async_chat.__init__(self, sock) - self.cycler = cycler - self.ibuffer = [] - self.set_terminator("\n") - - def collect_incoming_data(self, data): - self.ibuffer.append(data) - - def found_terminator(self): - line = "".join(self.ibuffer).lower() - self.ibuffer = [] - prefix, cmd = line.split(None, 1) - if prefix != "cmd": - logging.debug('Bad line received "%s"' % line) - return - if hasattr(self.cycler, "cmd_" + cmd): - logging.debug('Executing command "%s"' % cmd) - getattr(self.cycler, "cmd_" + cmd)() - else: - logging.debug('Unknown command received "%s"' % cmd) + def __init__(self, cycler, sock): + asynchat.async_chat.__init__(self, sock) + self.cycler = cycler + self.ibuffer = [] + self.set_terminator("\n") + + def collect_incoming_data(self, data): + self.ibuffer.append(data) + + def found_terminator(self): + line = "".join(self.ibuffer).lower() + self.ibuffer = [] + prefix, cmd = line.split(None, 1) + if prefix != "cmd": + logging.debug('Bad line received "%s"' % line) + return + if hasattr(self.cycler, "cmd_" + cmd): + logging.debug('Executing command "%s"' % cmd) + getattr(self.cycler, "cmd_" + cmd)() + else: + logging.debug('Unknown command received "%s"' % cmd) class SockHackWrap(object): - def __init__(self, sock, addr): - self.__sock = sock - self.__addr = addr - def getpeername(self): - return self.__addr - def __getattr__(self, key): - return getattr(self.__sock, key) + def __init__(self, sock, addr): + self.__sock = sock + self.__addr = addr + def getpeername(self): + return self.__addr + def __getattr__(self, key): + return getattr(self.__sock, key) class Listener(asyncore.dispatcher): - def __init__(self, socket_filename, cycler): - asyncore.dispatcher.__init__(self) - self.cycler = cycler - self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.bind(socket_filename) - self.listen(2) # Backlog = 2 - - def handle_accept(self): - sock, addr = self.accept() - Server(self.cycler, SockHackWrap(sock, addr)) - - def writable(self): - return False - + def __init__(self, socket_filename, cycler): + asyncore.dispatcher.__init__(self) + self.cycler = cycler + self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.bind(socket_filename) + self.listen(2) # Backlog = 2 + + def handle_accept(self): + sock, addr = self.accept() + Server(self.cycler, SockHackWrap(sock, addr)) + + def writable(self): + return False + def do_server(options, paths): - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(options.socket_filename) - print >>sys.stderr, "Server is already running! Sending exit command." - sock = sock.makefile() - sock.write("cmd exit\n") - sock.close() - except Exception, e: - pass - - try: - os.unlink(options.socket_filename) - except OSError: - pass - - cycler = Cycler() - listener = Listener(options.socket_filename, cycler) - # Initialisation of Cycler delayed so we grab the socket quickly - cycler.init(options, paths) - try: - asyncsched.loop() - except KeyboardInterrupt: - print - cycler.finish() + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(options.socket_filename) + print >>sys.stderr, "Server is already running! Sending exit command." + sock = sock.makefile() + sock.write("cmd exit\n") + sock.close() + except Exception, e: + pass + + try: + os.unlink(options.socket_filename) + except OSError: + pass + + cycler = Cycler() + listener = Listener(options.socket_filename, cycler) + # Initialisation of Cycler delayed so we grab the socket quickly + cycler.init(options, paths) + try: + asyncsched.loop() + except KeyboardInterrupt: + print + cycler.finish() def do_client(options, args): - if len(args) == 0: - args = ["next"] - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(options.socket_filename) - sock = sock.makefile() - for i, cmd in enumerate(args): - sock.write("cmd %s\n" % cmd) - if i < len(args) - 1: - time.sleep(options.cycle_time) - sock.close() + if len(args) == 0: + args = ["next"] + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(options.socket_filename) + sock = sock.makefile() + for i, cmd in enumerate(args): + sock.write("cmd %s\n" % cmd) + if i < len(args) - 1: + time.sleep(options.cycle_time) + sock.close() def do_oneshot(options, paths): - cycler = Cycler() - cycler.init(options, paths, oneshot=True) + cycler = Cycler() + cycler.init(options, paths, oneshot=True) def build_parser(): - parser = OptionParser(version="%prog " + VERSION, - description = "Cycles through random background images.", - usage = - "\n(server) %prog [options] dir [dir2 ...]" - "\n(client) %prog [options] [next|prev|rescan|reload|pause] [...]" - "\nThe first instance to be run will be the server.\n" - ) - parser.add_option("-v", '-d', "--verbose", "--debug", - action="count", dest="verbose", default=0, - help="Make the louder (good for debugging, or those who are curious)") - parser.add_option("-b", "--background-colour", - action="store", type="string", dest="background_colour", default="black", - help="Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %default]") - parser.add_option("--all-random", - action="store_true", dest="all_random", default=False, - help="Make sure that all images have been displayed before repeating an image") - parser.add_option("-1", "--oneshot", - action="store_true", dest="oneshot", default=False, - help="Set one random image and terminate immediately.") - parser.add_option("--folder-random", - action="store_true", dest="folder_random", default=False, - help="Give each folder an equal chance of having an image selected from it") - parser.add_option("--convert", - action="store_true", dest="convert", default=False, - help="Do conversions using ImageMagick or PIL, don't rely on the window manager") - parser.add_option("--cycle-time", - action="store", type="int", default=1800, dest="cycle_time", - help="Cause the image to cycle every X seconds") - parser.add_option("--socket", - action="store", type="string", dest="socket_filename", default=os.path.expanduser('~/.randombg_socket'), - help="Location of the command/control socket.") - parser.add_option("--cache-file", - action="store", type="string", dest="cache_filename", default=os.path.expanduser('~/.randombg_cache'), - help="Stores the location of the last image to be loaded.") - parser.add_option("--server", - action="store_true", dest="server", default=False, - help="Run in server mode to listen for clients.") - return parser + parser = OptionParser(version="%prog " + VERSION, + description = "Cycles through random background images.", + usage = + "\n(server) %prog [options] dir [dir2 ...]" + "\n(client) %prog [options] [next|prev|rescan|reload|pause] [...]" + "\nThe first instance to be run will be the server.\n" + ) + parser.add_option("-v", '-d', "--verbose", "--debug", + action="count", dest="verbose", default=0, + help="Make the louder (good for debugging, or those who are curious)") + parser.add_option("-b", "--background-colour", + action="store", type="string", dest="background_colour", default="black", + help="Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %default]") + parser.add_option("--all-random", + action="store_true", dest="all_random", default=False, + help="Make sure that all images have been displayed before repeating an image") + parser.add_option("-1", "--oneshot", + action="store_true", dest="oneshot", default=False, + help="Set one random image and terminate immediately.") + parser.add_option("--folder-random", + action="store_true", dest="folder_random", default=False, + help="Give each folder an equal chance of having an image selected from it") + parser.add_option("--convert", + action="store_true", dest="convert", default=False, + help="Do conversions using ImageMagick or PIL, don't rely on the window manager") + parser.add_option("--cycle-time", + action="store", type="int", default=1800, dest="cycle_time", + help="Cause the image to cycle every X seconds") + parser.add_option("--socket", + action="store", type="string", dest="socket_filename", default=os.path.expanduser('~/.randombg_socket'), + help="Location of the command/control socket.") + parser.add_option("--cache-file", + action="store", type="string", dest="cache_filename", default=os.path.expanduser('~/.randombg_cache'), + help="Stores the location of the last image to be loaded.") + parser.add_option("--server", + action="store_true", dest="server", default=False, + help="Run in server mode to listen for clients.") + return parser def main(): - parser = build_parser() - options, args = parser.parse_args(sys.argv[1:]) + parser = build_parser() + options, args = parser.parse_args(sys.argv[1:]) - if options.verbose == 1: - logging.getLogger().setLevel(logging.INFO) - elif options.verbose >= 2: - logging.getLogger().setLevel(logging.DEBUG) - - if options.server: - do_server(options, args) - return + if options.verbose == 1: + logging.getLogger().setLevel(logging.INFO) + elif options.verbose >= 2: + logging.getLogger().setLevel(logging.DEBUG) + + if options.server: + do_server(options, args) + return - if options.oneshot: - do_oneshot(options, args) - return + if options.oneshot: + do_oneshot(options, args) + return - try: - do_client(options, args) - return - except Exception, e: - print >>sys.stderr, "Failed to connect to server:", e + try: + do_client(options, args) + return + except Exception, e: + print >>sys.stderr, "Failed to connect to server:", e if __name__ == "__main__": - main() + main() diff --git a/wallchanger.py b/wallchanger.py index 2bbd6f9..8bfb69b 100755 --- a/wallchanger.py +++ b/wallchanger.py @@ -8,9 +8,9 @@ import commands, sys, os, os.path, time import logging try: - import PIL, PIL.Image + import PIL, PIL.Image except ImportError: - PIL = None + PIL = None __all__ = ("init", "set_image") @@ -18,275 +18,275 @@ __all__ = ("init", "set_image") changers = [] def set_image(filename): - logging.info("Setting image: %s", filename) - for changer in changers: - if not changer.set_image(filename): - logging.warning("Failed to set background: wallchanger.set_image(%s), changer=%s", filename, changer) + logging.info("Setting image: %s", filename) + for changer in changers: + if not changer.set_image(filename): + logging.warning("Failed to set background: wallchanger.set_image(%s), changer=%s", filename, changer) def check_cmd(cmd): - return commands.getstatusoutput(cmd)[0] == 0 + return commands.getstatusoutput(cmd)[0] == 0 def init(*args, **kwargs): - """Desktop Changer factory""" + """Desktop Changer factory""" - classes = [] + classes = [] - if sys.platform == "win32": - classes.append(WIN32Changer) - return + if sys.platform == "win32": + classes.append(WIN32Changer) + return - logging.debug("Testing for OSX (NonX11)") - if check_cmd("ps ax -o command -c|grep -q WindowServer"): - classes.append(OSXChanger) + logging.debug("Testing for OSX (NonX11)") + if check_cmd("ps ax -o command -c|grep -q WindowServer"): + classes.append(OSXChanger) - if 'DISPLAY' not in os.environ or os.environ['DISPLAY'].startswith('/tmp/launch'): - # X11 is not running - return - else: - if os.uname()[0] == 'Darwin': - # Try to detect if the X11 server is running on OSX - if check_cmd("ps ax -o command|grep -q '^/.*X11 .* %s'" % os.environ['DISPLAY']): - # X11 is not running for this display - return + if 'DISPLAY' not in os.environ or os.environ['DISPLAY'].startswith('/tmp/launch'): + # X11 is not running + return + else: + if os.uname()[0] == 'Darwin': + # Try to detect if the X11 server is running on OSX + if check_cmd("ps ax -o command|grep -q '^/.*X11 .* %s'" % os.environ['DISPLAY']): + # X11 is not running for this display + return - logging.debug("Testing for XFCE4") - if check_cmd("xwininfo -name 'xfce4-session'"): - classes.append(Xfce4Changer) + logging.debug("Testing for XFCE4") + if check_cmd("xwininfo -name 'xfce4-session'"): + classes.append(Xfce4Changer) - logging.debug("Testing for Gnome") - if check_cmd("xwininfo -name 'gnome-settings-daemon'"): - if check_cmd("gsettings get org.gnome.desktop.background picture-uri"): - classes.append(Gnome3Changer) - else: - classes.append(Gnome2Changer) + logging.debug("Testing for Gnome") + if check_cmd("xwininfo -name 'gnome-settings-daemon'"): + if check_cmd("gsettings get org.gnome.desktop.background picture-uri"): + classes.append(Gnome3Changer) + else: + classes.append(Gnome2Changer) - logging.debug("Testing for xloadimage") - if check_cmd("which xloadimage"): - classes.append(XLoadImageChanger) + logging.debug("Testing for xloadimage") + if check_cmd("which xloadimage"): + classes.append(XLoadImageChanger) - if len(classes) == 0: - raise Exception("Unknown window manager") + if len(classes) == 0: + raise Exception("Unknown window manager") - for klass in classes: - changers.append(klass(*args, **kwargs)) + for klass in classes: + changers.append(klass(*args, **kwargs)) class BaseChanger(object): - name = "undefined" - def __init__(self, background_color='black', convert=False): - logging.info('Determined the window manager is "%s"', self.name) - self.background_color = background_color - self.convert = convert - - try: - def _exec_cmd(self, cmd): - import subprocess - return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() - - except ImportError: - # A simple implementation of subprocess for python2.4 - def _exec_cmd(self, cmd): - """Runs a program given in cmd""" - return os.spawnvp(os.P_WAIT, cmd[0], cmd) - - def set_image(self, filename): - raise NotImplementedError() - - def convert_image_format(self, filename, format='BMP', allowAlpha=False, extension='.bmp'): - """Convert the image to another format, and store it in a local place""" - if not os.path.exists(filename): - logger.warn('The input file "%s" does not exist, so it will not be converted', filename) - return filename, False - if PIL is None: - logger.warn('PIL could not be found, not converting image format') - return filename, False - - self.remove_old_image_cache() - output_name = os.path.join(self._ConvertedWallpaperLocation, '%s%s' % (time.time(), extension)) - img = PIL.Image.open(filename) - - # Remove the alpha channel if the user doens't want it - if not allowAlpha and img.mode == 'RGBA': - img = img.convert('RGB') - img.save(output_name, format) - - return output_name, True + name = "undefined" + def __init__(self, background_color='black', convert=False): + logging.info('Determined the window manager is "%s"', self.name) + self.background_color = background_color + self.convert = convert + + try: + def _exec_cmd(self, cmd): + import subprocess + return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() + + except ImportError: + # A simple implementation of subprocess for python2.4 + def _exec_cmd(self, cmd): + """Runs a program given in cmd""" + return os.spawnvp(os.P_WAIT, cmd[0], cmd) + + def set_image(self, filename): + raise NotImplementedError() + + def convert_image_format(self, filename, format='BMP', allowAlpha=False, extension='.bmp'): + """Convert the image to another format, and store it in a local place""" + if not os.path.exists(filename): + logger.warn('The input file "%s" does not exist, so it will not be converted', filename) + return filename, False + if PIL is None: + logger.warn('PIL could not be found, not converting image format') + return filename, False + + self.remove_old_image_cache() + output_name = os.path.join(self._ConvertedWallpaperLocation, '%s%s' % (time.time(), extension)) + img = PIL.Image.open(filename) + + # Remove the alpha channel if the user doens't want it + if not allowAlpha and img.mode == 'RGBA': + img = img.convert('RGB') + img.save(output_name, format) + + return output_name, True class XLoadImageChanger(BaseChanger): - name = "xloadimage" - _ConvertedWallpaperLocation = '/tmp/wallpapers_xloadimage/' - def remove_old_image_cache(self): - """Cleans up any old temp images""" - if not os.path.isdir(self._ConvertedWallpaperLocation): - os.mkdir(self._ConvertedWallpaperLocation) - for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): - for filename in filenames: - os.unlink(os.path.join(fullpath, filename)) - for dirname in dirnames: - os.unlink(os.path.join(fullpath, dirname)) - - def convert_image_format(self, filename): - """Convert the image to a png, and store it in a local place""" - self.remove_old_image_cache() - output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) - cmd = ["convert", filename, output_name] - logging.debug("""Convert command: '"%s"'""", '" "'.join(cmd)) - return output_name, self._exec_cmd(cmd) - - def set_image(self, filename): - if self.convert: - filename, convert_status = self.convert_image_format(filename) - if convert_status: - logging.debug('Convert failed') - cmd = [ - "xloadimage", - "-onroot", - "-fullscreen", - "-border", "black", - filename, - ] - logging.debug('''WMaker bgset command: "'%s'"''', "' '".join(cmd)) - return not self._exec_cmd(cmd) + name = "xloadimage" + _ConvertedWallpaperLocation = '/tmp/wallpapers_xloadimage/' + def remove_old_image_cache(self): + """Cleans up any old temp images""" + if not os.path.isdir(self._ConvertedWallpaperLocation): + os.mkdir(self._ConvertedWallpaperLocation) + for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): + for filename in filenames: + os.unlink(os.path.join(fullpath, filename)) + for dirname in dirnames: + os.unlink(os.path.join(fullpath, dirname)) + + def convert_image_format(self, filename): + """Convert the image to a png, and store it in a local place""" + self.remove_old_image_cache() + output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) + cmd = ["convert", filename, output_name] + logging.debug("""Convert command: '"%s"'""", '" "'.join(cmd)) + return output_name, self._exec_cmd(cmd) + + def set_image(self, filename): + if self.convert: + filename, convert_status = self.convert_image_format(filename) + if convert_status: + logging.debug('Convert failed') + cmd = [ + "xloadimage", + "-onroot", + "-fullscreen", + "-border", "black", + filename, + ] + logging.debug('''WMaker bgset command: "'%s'"''', "' '".join(cmd)) + return not self._exec_cmd(cmd) class OSXChanger(BaseChanger): - name = "Mac OS X" - _ConvertedWallpaperLocation = '/tmp/wallpapers/' - _DesktopPlistLocation = os.path.expanduser('~/Library/Preferences/com.apple.desktop.plist') - - def __init__(self, *args, **kwargs): - BaseChanger.__init__(self, *args, **kwargs) - - def remove_old_image_cache(self): - """Cleans up any old temp images""" - if not os.path.isdir(self._ConvertedWallpaperLocation): - os.mkdir(self._ConvertedWallpaperLocation) - for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): - for filename in filenames: - os.unlink(os.path.join(fullpath, filename)) - for dirname in dirnames: - os.unlink(os.path.join(fullpath, dirname)) - - def convert_image_format(self, filename): - """Convert the image to a png, and store it in a local place""" - self.remove_old_image_cache() - output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) - try: - return super(OSXChanger, self).convert_image_format(filename, format='PNG', extension='.png') - except ImportError: - logging.debug('Could not load PIL, going to try just copying the image') - import shutil - output_name = os.path.join(self._ConvertedWallpaperLocation, os.path.basename(filename)) - shutil.copyfile(filename, output_name) - return output_name, True - - def fix_desktop_plist(self): - """Removes the entry in the desktop plist file that specifies the wallpaper for each monitor""" - try: - import Foundation - desktop_plist = Foundation.NSMutableDictionary.dictionaryWithContentsOfFile_(self._DesktopPlistLocation) - # Remove all but the 'default' entry - for k in desktop_plist['Background'].keys(): - if k == 'default': - continue - desktop_plist['Background'].removeObjectForKey_(k) - # Store the plist again (Make sure we write it out atomically -- Don't want to break finder) - desktop_plist.writeToFile_atomically_(self._DesktopPlistLocation, True) - except ImportError: - logging.debug('Could not import the Foundation module, you may have problems with dual screens') - - def set_image(self, filename): - self.fix_desktop_plist() - if self.convert: - filename, ret = self.convert_image_format(filename) - if not ret: - logging.debug("Convert failed") - return False - cmd = """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % filename - logging.debug(cmd) - return not commands.getstatusoutput(cmd)[0] + name = "Mac OS X" + _ConvertedWallpaperLocation = '/tmp/wallpapers/' + _DesktopPlistLocation = os.path.expanduser('~/Library/Preferences/com.apple.desktop.plist') + + def __init__(self, *args, **kwargs): + BaseChanger.__init__(self, *args, **kwargs) + + def remove_old_image_cache(self): + """Cleans up any old temp images""" + if not os.path.isdir(self._ConvertedWallpaperLocation): + os.mkdir(self._ConvertedWallpaperLocation) + for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): + for filename in filenames: + os.unlink(os.path.join(fullpath, filename)) + for dirname in dirnames: + os.unlink(os.path.join(fullpath, dirname)) + + def convert_image_format(self, filename): + """Convert the image to a png, and store it in a local place""" + self.remove_old_image_cache() + output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) + try: + return super(OSXChanger, self).convert_image_format(filename, format='PNG', extension='.png') + except ImportError: + logging.debug('Could not load PIL, going to try just copying the image') + import shutil + output_name = os.path.join(self._ConvertedWallpaperLocation, os.path.basename(filename)) + shutil.copyfile(filename, output_name) + return output_name, True + + def fix_desktop_plist(self): + """Removes the entry in the desktop plist file that specifies the wallpaper for each monitor""" + try: + import Foundation + desktop_plist = Foundation.NSMutableDictionary.dictionaryWithContentsOfFile_(self._DesktopPlistLocation) + # Remove all but the 'default' entry + for k in desktop_plist['Background'].keys(): + if k == 'default': + continue + desktop_plist['Background'].removeObjectForKey_(k) + # Store the plist again (Make sure we write it out atomically -- Don't want to break finder) + desktop_plist.writeToFile_atomically_(self._DesktopPlistLocation, True) + except ImportError: + logging.debug('Could not import the Foundation module, you may have problems with dual screens') + + def set_image(self, filename): + self.fix_desktop_plist() + if self.convert: + filename, ret = self.convert_image_format(filename) + if not ret: + logging.debug("Convert failed") + return False + cmd = """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % filename + logging.debug(cmd) + return not commands.getstatusoutput(cmd)[0] class WIN32Changer(BaseChanger): - name = "Windows" - _ConvertedWallpaperLocation = os.path.join(os.environ.get('APPDATA', os.path.expanduser('~')), 'wallchanger') - - def __init__(self, *args, **kwargs): - BaseChanger.__init__(self, *args, **kwargs) - if not self.convert: - logging.warn('Running on windows, but convert is not set') - - def remove_old_image_cache(self): - """Cleans up any old temp images""" - if not os.path.isdir(self._ConvertedWallpaperLocation): - os.mkdir(self._ConvertedWallpaperLocation) - for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): - for filename in filenames: - os.unlink(os.path.join(fullpath, filename)) - for dirname in dirnames: - os.unlink(os.path.join(fullpath, dirname)) - - def set_image(self, filename): - import ctypes - user32 = ctypes.windll.user32 - - # Taken from the Platform SDK - SPI_SETDESKWALLPAPER = 20 - SPIF_SENDWININICHANGE = 2 - - if self.convert: - filename, ret = self.convert_image_format(filename) - if not ret: - logging.debug("Convert failed") - return False - - # Parameters for SystemParametersInfoA are: - # (UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni) - user32.SystemParametersInfoA( - SPI_SETDESKWALLPAPER, - 0, - filename, - SPIF_SENDWININICHANGE, - ) - return True + name = "Windows" + _ConvertedWallpaperLocation = os.path.join(os.environ.get('APPDATA', os.path.expanduser('~')), 'wallchanger') + + def __init__(self, *args, **kwargs): + BaseChanger.__init__(self, *args, **kwargs) + if not self.convert: + logging.warn('Running on windows, but convert is not set') + + def remove_old_image_cache(self): + """Cleans up any old temp images""" + if not os.path.isdir(self._ConvertedWallpaperLocation): + os.mkdir(self._ConvertedWallpaperLocation) + for fullpath, filenames, dirnames in os.walk(self._ConvertedWallpaperLocation, topdown=False): + for filename in filenames: + os.unlink(os.path.join(fullpath, filename)) + for dirname in dirnames: + os.unlink(os.path.join(fullpath, dirname)) + + def set_image(self, filename): + import ctypes + user32 = ctypes.windll.user32 + + # Taken from the Platform SDK + SPI_SETDESKWALLPAPER = 20 + SPIF_SENDWININICHANGE = 2 + + if self.convert: + filename, ret = self.convert_image_format(filename) + if not ret: + logging.debug("Convert failed") + return False + + # Parameters for SystemParametersInfoA are: + # (UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni) + user32.SystemParametersInfoA( + SPI_SETDESKWALLPAPER, + 0, + filename, + SPIF_SENDWININICHANGE, + ) + return True class Gnome2Changer(BaseChanger): - name = "Gnome" - def set_image(self, filename): - cmd = ['gconftool-2', '--type', 'string', '--set', '/desktop/gnome/background/picture_filename', filename] - logging.debug(cmd) - return not self._exec_cmd(cmd) + name = "Gnome" + def set_image(self, filename): + cmd = ['gconftool-2', '--type', 'string', '--set', '/desktop/gnome/background/picture_filename', filename] + logging.debug(cmd) + return not self._exec_cmd(cmd) class Gnome3Changer(BaseChanger): - name = "Gnome3" - def set_image(self, filename): - cmd = ['gsettings', 'set', 'org.gnome.desktop.background', 'picture-uri', 'file://'+filename] - logging.debug(cmd) - return not self._exec_cmd(cmd) + name = "Gnome3" + def set_image(self, filename): + cmd = ['gsettings', 'set', 'org.gnome.desktop.background', 'picture-uri', 'file://'+filename] + logging.debug(cmd) + return not self._exec_cmd(cmd) class Xfce4Changer(BaseChanger): - name = "XFCE4" - def set_image(self, filename): - cmd = [ - "xfconf-query", - "-c", "xfce4-desktop", - "-p", "/backdrop/screen0/monitor0/image-path", - "-s", filename, - ] - logging.debug(cmd) - return not self._exec_cmd(cmd) + name = "XFCE4" + def set_image(self, filename): + cmd = [ + "xfconf-query", + "-c", "xfce4-desktop", + "-p", "/backdrop/screen0/monitor0/image-path", + "-s", filename, + ] + logging.debug(cmd) + return not self._exec_cmd(cmd) def main(filename): - logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s") - init() - set_image(filename) + logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s") + init() + set_image(filename) if __name__ == "__main__": - try: - filename = sys.argv[1] - except: - print >>sys.stderr, "Usage: %s filename" % sys.argv[0] - sys.exit(1) + try: + filename = sys.argv[1] + except: + print >>sys.stderr, "Usage: %s filename" % sys.argv[0] + sys.exit(1) - main(filename) + main(filename) -- 2.39.2