view bing4desktop/bing4desktop.py @ 26:67b4b7916036

bing4desktop-0.240208-0.2
author prymula <prymula76@outlook.com>
date Thu, 08 Feb 2024 11:56:27 +0100
parents 45b565b7b853
children 4d13cfa90272
line wrap: on
line source

#!/usr/bin/env python3
# bing4desktop.py - skrypt tray dla Cinnamon oraz Matte i innych WM
# inspiracja: https://pystray.readthedocs.io/en/latest/usage.html
# oraz 'zasłyszane' w internecie :)
# licencja: Public Domain
# edytor: Geany
# data: 05-II-2024
# napisał: Prymula

# python3-pillow-tk

import pystray
from pystray import Icon as icon, Menu as menu, MenuItem as item
import tkinter as tk
from tkinter import messagebox
from PIL import ImageTk, Image

import os, sys, subprocess
import requests
import json
import tempfile
import shutil
import glob

ver = "0.240207-5"

BING_URI_BASE = "http://www.bing.com"
BING_WALLPAPER_PATH = "/HPImageArchive.aspx?format=js&idx=1&n=1&mkt=en-US"
full_path=""
actualImage = 1
COPYRIGHT_TXT = ""
TITLE = ""
TMP_COPYRIGHT = ""
TMP_TITLE = ""

tmp_path =  tempfile.mkdtemp()

class WPaper:
    def get(self):
        global full_path, COPYRIGHT_TXT, TITLE
        # open the Bing HPImageArchive URI and ask for a JSON response
        resp = requests.get(BING_URI_BASE + BING_WALLPAPER_PATH)

        if resp.status_code == 200:
            json_response = json.loads(resp.content)
            wallpaper_path = json_response['images'][0]['url']
            COPYRIGHT_TXT = json_response['images'][0]['copyright']
            TITLE = json_response['images'][0]['title']

            filename = wallpaper_path.split('/')[-1]
            wallpaper_uri = BING_URI_BASE + wallpaper_path
            filename_short = filename.split('&')
            print ("filemname_short", filename_short[0])
            # open the actual wallpaper uri, and write the response as an image on the filesystem
            response = requests.get(wallpaper_uri)
            if resp.status_code == 200:
                desktop_env = os.environ.get('DESKTOP_SESSION')
                if desktop_env=="windows":
                    messagebox.showinfo("Błąd","Brak wsparcia dla Windows i Mac !")
                    cleanup()
                    exit
                else:
                    full_path = tmp_path + filename_short[0]
                    with open(full_path, 'wb') as f:
                        f.write(response.content)
            else:
                raise ValueError("[ERROR] non-200 response from Bing server for '{}'".format(wallpaper_uri))
                COPYRIGHT_TXT = "[ERROR] non-200 response from Bing server for " + str(format(wallpaper_uri))
                return False
        else:
            raise ValueError("[ERROR] non-200 response from Bing server for '{}'".format(BING_URI_BASE + BING_WALLPAPER_PATH))
            COPYRIGHT_TXT = "[ERROR] non-200 response from Bing server for " + str(format(BING_URI_BASE + BING_WALLPAPER_PATH))
            return False
        return True
        
    def set(self,file_loc, first_run):
        global full_path, TMP_COPYRIGHT, COPYRIGHT_TXT, TMP_TITLE, TITLE
        desktop_env = os.environ.get('DESKTOP_SESSION')

        TMP_COPYRIGHT = COPYRIGHT_TXT
        TMP_TITLE = TITLE

        if len(full_path) == 0:
            messagebox.showinfo("Błąd","Niewybrano obrazu, najpierw wybierz obraz")
            return
        try:
            if desktop_env in ["gnome-classic", "gnome", "unity", "default"]:
                uri = "'file://%s'" % file_loc
                try:
                    SCHEMA = "org.gnome.desktop.background"
                    KEY = "picture-uri"
                    gsettings = Gio.Settings.new(SCHEMA)
                    gsettings.set_string(KEY, uri)
                except:
                    args = ["gsettings", "set", "org.gnome.desktop.background", "picture-uri", uri]
                    subprocess.Popen(args)
            elif desktop_env in ["cinnamon"]:
                uri = "'file://%s'" % file_loc
                try:
                    SCHEMA = "org.cinnamon.desktop.background"
                    KEY = "picture-uri"
                    gsettings = Gio.Settings.new(SCHEMA)
                    gsettings.set_string(KEY, uri)
                except:
                    args = ["gsettings", "set", "org.cinnamon.desktop.background", "picture-uri", uri]
                    subprocess.Popen(args)
            elif desktop_env in ['mate', 'lightdm-xsession']:
                try: # MATE >= 1.6
                    # info from http://wiki.mate-desktop.org/docs:gsettings
                    args = ["gsettings", "set", "org.mate.background", "picture-filename", "'%s'" % file_loc]
                    subprocess.Popen(args)
                except: # MATE < 1.6
                    # From https://bugs.launchpad.net/variety/+bug/1033918
                    args = ["mateconftool-2","-t","string","--set","/desktop/mate/background/picture_filename",'"%s"' %file_loc]
                    subprocess.Popen(args)
            elif desktop_env=="gnome2": # Not tested
                # From https://bugs.launchpad.net/variety/+bug/1033918
                args = ["gconftool-2","-t","string","--set","/desktop/gnome/background/picture_filename", '"%s"' %file_loc]
                subprocess.Popen(args)
            ## KDE4 is difficult
            ## see http://blog.zx2c4.com/699 for a solution that might work
            elif desktop_env in ["kde3", "trinity"]:
                # From http://ubuntuforums.org/archive/index.php/t-803417.html
                args = 'dcop kdesktop KBackgroundIface setWallpaper 0 "%s" 6' % file_loc
                subprocess.Popen(args,shell=True)

            elif desktop_env=="xfce4":
                #From http://www.commandlinefu.com/commands/view/2055/change-wallpaper-for-xfce4-4.6.0
                if first_run:
                    args0 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-path", "-s", file_loc]
                    args1 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-style", "-s", "3"]
                    args2 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-show", "-s", "true"]
                    subprocess.Popen(args0)
                    subprocess.Popen(args1)
                    subprocess.Popen(args2)
                args = ["xfdesktop","--reload"]
                subprocess.Popen(args)

            elif desktop_env in ["fluxbox","jwm","openbox","afterstep"]:
                #http://fluxbox-wiki.org/index.php/Howto_set_the_background
                # used fbsetbg on jwm too since I am too lazy to edit the XML configuration
                # now where fbsetbg does the job excellent anyway.
                # and I have not figured out how else it can be set on Openbox and AfterSTep
                # but fbsetbg works excellent here too.
                try:
                    args = ["fbsetbg", file_loc]
                    subprocess.Popen(args)
                except:
                    sys.stderr.write("ERROR: Failed to set wallpaper with fbsetbg!\n")
                    sys.stderr.write("Please make sre that You have fbsetbg installed.\n")
            elif desktop_env=="icewm":
                # command found at http://urukrama.wordpress.com/2007/12/05/desktop-backgrounds-in-window-managers/
                args = ["icewmbg", file_loc]
                subprocess.Popen(args)
            elif desktop_env=="blackbox":
                # command found at http://blackboxwm.sourceforge.net/BlackboxDocumentation/BlackboxBackground
                args = ["bsetbg", "-full", file_loc]
                subprocess.Popen(args)
            elif desktop_env=="lxde":
                args = "pcmanfm --set-wallpaper %s --wallpaper-mode=scaled" % file_loc
                subprocess.Popen(args,shell=True)
            elif desktop_env=="WindowMaker":
                # From http://www.commandlinefu.com/commands/view/3857/set-wallpaper-on-windowmaker-in-one-line
                args = "wmsetbg -s -u %s" % file_loc
                subprocess.Popen(args,shell=True)
            elif desktop_env=="windows": #Not tested since I do not run this on Windows
                #From https://stackoverflow.com/questions/1977694/change-desktop-background
                import ctypes
                SPI_SETDESKWALLPAPER = 20
                ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, file_loc , 0)
            else:
                if first_run: #don't spam the user with the same message over and over again
                    sys.stderr.write("Warning: Failed to set wallpaper. Your desktop environment is not supported.")
                    sys.stderr.write("You can try manually to set Your wallpaper to %s" % file_loc)
                return False
            return True
        except:
            sys.stderr.write("ERROR: Failed to set wallpaper. There might be a bug.\n")
            return False


class Gui():
    global TMP_COPYRIGHT, TMP_TITLE
    
    def __init__(self):
        self.window = tk.Tk()
        self.image = None
        for ico in ['./bing.ico', '/usr/share/bing4desktop/bing.ico']:
            try:
                self.image =  Image.open(ico)
            except:
                print ('W bieżącym katalogu, brak ikony ' + ico)
            finally:
                if self.image != None:
                    break
            
        self.tmp = None
        self.menu = (
            item("Ustaw Tapetę", lambda icon, item: wp.set(full_path, True)),
            item('Tapeta poprzednia', lambda icon, item: self.change_pic(1, icon)),
            item('Tapeta następna', lambda icon, item: self.change_pic(-1, icon)),
            item('Opis zdjęcia', lambda icon, item: icon.notify(TMP_TITLE + ' --- ' + TMP_COPYRIGHT)),
            item('O Programie', lambda icon, item: self.about()),

            item('Wyjdź', self.quit_window)
            )
        self.frame = tk.Frame(self.window, width=600, height=400)
        self.frame.pack()
        self.frame.place(anchor='center', relx=0.5, rely=5)
        
        #self.window.title("Bing4Desktop")
        #self.window.geometry('800x600')
        #self.window.protocol('WM_DELETE_WINDOW', self.withdraw_window)
        self.withdraw_window()
        self.window.mainloop()
   
    def about(self):
        global ver
        messagebox.showinfo("Bing4Desktop","Inspiracja:\npystray.readthedocs.io/en/latest/usage.html\n\nNapisał: Prymula\n\nLuty 2024\n\nLicencja:\nPublic Domain\n\nWersja: "+ver)
        # możliwe że nieprawidlowe po wyjściu z aplikacji zwróci bląd :(
        self.window.destroy()
        self.window.update()

    def change_pic(self, n, icon):
        global actualImage, BING_WALLPAPER_PATH, TITLE, COPYRIGHT_TXT
        BASE = "/HPImageArchive.aspx?format=js&idx="
        OFF = "&n=1&mkt=en-US"
        
        actualImage = actualImage + n;
        if (actualImage < 0):
             actualImage = 7
        elif (actualImage > 7):
             actualImage = 0
             
        wp=WPaper()
        if wp.get():
            BING_WALLPAPER_PATH = BASE+str(actualImage)+OFF
            if wp.get() == False:
                cleanup()
                exit
            
            # Wszystko pięknie tylko metoda 'show', zmienia nazwę pliku przechowywanego w TMP
            # Pozatym nie ubija a nie znalazłem nic lepszego
            print ("GLOB: ", glob.glob( os.path.join('/tmp', '*.PNG')))
            for infile in glob.glob( os.path.join('/tmp', '*.PNG')):
                print ("INFILE: ", infile)
                viewer = subprocess.Popen(['/usr/bin/eom', infile])
                viewer.terminate()
                viewer.kill()  # make sure the viewer is gone; not needed on Windows  
                
            img = Image.open(full_path) #.convert('RGBA')  # PIL solution
            self.tmp = img.resize((600, 400), None, None, 3.0)
            self.tmp.show()
            icon.notify(TITLE + ' --- ' + COPYRIGHT_TXT)
            
    def quit_window(self):
        self.icon.stop()
        self.window.destroy()

    def show_window(self):
        self.icon.stop()
        self.window.protocol('WM_DELETE_WINDOW', self.withdraw_window)
        self.window.after(0, self.window.deiconify)

    def withdraw_window(self):
        self.window.withdraw()
        self.icon = pystray.Icon("name", self.image, "title", self.menu)
        self.icon.run()

def cleanup():
    # NIEBEZPIECZNA ! lepiej niczym innym nie nadpisywać tych zmiennych
    shutil.rmtree(tmp_path)    
        
if __name__ in '__main__':
    wp=WPaper()
    if wp.get():
        wp.set(full_path, True)
        g = Gui()
        cleanup()