如何使用Python更改桌面背景?

52

我该如何使用Python更改我的桌面背景?

我想在Windows和Linux系统中实现此操作。


2
对于你提出的关于 Linux 的问题(假设你使用 GNOME 桌面环境),你可能想要查看 http://oracle.bridgewayconsulting.com.au/~danni/misc/change-background-py.html。 - unutbu
有人知道如何在KDE中实现这个吗? - TheInitializer
15个回答

43

在 Windows 平台上,如果你使用的是 python2.5 或更高版本,可以使用 ctypes 加载 user32.dll,并调用 SystemParametersInfo() 函数并设置 SPI_SETDESKWALLPAPER 参数来设置桌面壁纸。

例如:

import ctypes
SPI_SETDESKWALLPAPER = 20 
ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, "image.jpg" , 0)

3
似乎无法使用 .jpg 格式,在 XP 上可以使用 .bmp 格式。 - Noelkd
2
我成功在Win 7上使用了一张JPG。 - CrazyPenguin
1
似乎在3.4版本上无法正常工作 ctypes.windll中不包含user32方法/函数 - CularBytes
2
我建议在Python2和Python3上都使用SystemParametersInfoW(并确保您尝试设置的路径在python2上正确解码为unicode)。 (W变体采用Unicode参数 - W代表宽字符,而A变体采用ANSI参数)。 - ejm
此外,对于最后一个参数,您可以使用值“3”,该值来自“SPIF_UPDATEINIFILE | SPIF_SENDCHANGE”(在https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-systemparametersinfow上搜索这些常量以获取有关它们的更多信息;这些值可以在“win32con”模块中找到 - 尽管没有必要依赖该模块仅获取这些常量值)。 - ejm
显示剩余3条评论

42

对于Python3.5,SystemParametersInfoA不起作用。 请使用SystemParametersInfoW。

import ctypes
ctypes.windll.user32.SystemParametersInfoW(20, 0, "absolute path" , 0)

是Python还是使用的Windows类型? - Johnny
3
Python2使用SystemParametersInfoA,Python3使用SystemParametersInfoW - mesksr
3
为了澄清SystemParametersInfoASystemParametersInfoW之间的区别,后者需要一个Unicode参数(即Wide char),前者需要一个ANSI参数。在Python2中您仍然可以使用*W变量,但请确保传递一个Unicode字符串(例如u"path"或str.decode(...)),而不是一个str(与Python3中的bytes相同)。 - ejm

21

我在我的一个初始项目中使用以下方法:

    def set_wallpaper(self,file_loc, first_run):
        # Note: There are two common Linux desktop environments where 
        # I have not been able to set the desktop background from 
        # command line: KDE, Enlightenment
        desktop_env = self.get_desktop_environment()
        try:
            if desktop_env in ["gnome", "unity", "cinnamon"]:
                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=="mate":
                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=="razor-qt": #TODO: implement reload of desktop when possible
                if first_run:
                    desktop_conf = configparser.ConfigParser()
                    # Development version
                    desktop_conf_file = os.path.join(self.get_config_dir("razor"),"desktop.conf") 
                    if os.path.isfile(desktop_conf_file):
                        config_option = r"screens\1\desktops\1\wallpaper"
                    else:
                        desktop_conf_file = os.path.join(self.get_home_dir(),".razor/desktop.conf")
                        config_option = r"desktops\1\wallpaper"
                    desktop_conf.read(os.path.join(desktop_conf_file))
                    try:
                        if desktop_conf.has_option("razor",config_option): #only replacing a value
                            desktop_conf.set("razor",config_option,file_loc)
                            with codecs.open(desktop_conf_file, "w", encoding="utf-8", errors="replace") as f:
                                desktop_conf.write(f)
                    except:
                        pass
                else:
                    #TODO: reload desktop when possible
                    pass 
            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)
            ## NOT TESTED BELOW - don't want to mess things up ##
            #elif desktop_env=="enlightenment": # I have not been able to make it work on e17. On e16 it would have been something in this direction
            #    args = "enlightenment_remote -desktop-bg-add 0 0 0 0 %s" % file_loc
            #    subprocess.Popen(args,shell=True)
            #elif desktop_env=="windows": #Not tested since I do not run this on Windows
            #    #From https://dev59.com/IXI-5IYBdhLWcg3wMFS0
            #    import ctypes
            #    SPI_SETDESKWALLPAPER = 20
            #    ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, file_loc , 0)
            #elif desktop_env=="mac": #Not tested since I do not have a mac
            #    #From https://dev59.com/CHRC5IYBdhLWcg3wCMnX
            #    try:
            #        from appscript import app, mactypes
            #        app('Finder').desktop_picture.set(mactypes.File(file_loc))
            #    except ImportError:
            #        #import subprocess
            #        SCRIPT = """/usr/bin/osascript<<END
            #        tell application "Finder" to
            #        set desktop picture to POSIX file "%s"
            #        end tell
            #        END"""
            #        subprocess.Popen(SCRIPT%file_loc, shell=True)
            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

    def get_config_dir(self, app_name=APP_NAME):
        if "XDG_CONFIG_HOME" in os.environ:
            confighome = os.environ['XDG_CONFIG_HOME'] 
        elif "APPDATA" in os.environ: # On Windows
            confighome = os.environ['APPDATA'] 
        else:
            try:
                from xdg import BaseDirectory   
                confighome =  BaseDirectory.xdg_config_home
            except ImportError: # Most likely a Linux/Unix system anyway
                confighome =  os.path.join(self.get_home_dir(),".config")
        configdir = os.path.join(confighome,app_name)
        return configdir

    def get_home_dir(self):
        if sys.platform == "cygwin":
            home_dir = os.getenv('HOME')
        else:
            home_dir = os.getenv('USERPROFILE') or os.getenv('HOME')
        if home_dir is not None:
            return os.path.normpath(home_dir)
        else:
            raise KeyError("Neither USERPROFILE or HOME environment variables set.")

在另一个线程中发布了get_desktop_environment方法


4
可以使用 os.path.expanduser('~') 代替 get_home_dir() 函数。 - user5413945
我已经更新了这段代码以适应Python 3,并在Ubuntu 22、Windows 10和macOS 10.14上使其正常工作,并通过避免插入shell命令来加强了安全性:https://github.com/1j01/textual-paint/blob/main/src/textual_paint/wallpaper.py - undefined

14

在 Gnome 桌面上,通常可以使用 gconf 完成此操作,可以直接调用 gconftool 或使用 gconf Python 模块。后者在 unutbu 给出的链接中提供了相关信息。第一种方法可像这样完成。

import commands
command = "gconftool-2 --set /desktop/gnome/background/picture_filename --type string '/path/to/file.jpg'"
status, output = commands.getstatusoutput(command)  # status=0 if success

对于Ubuntu 11.04,这似乎不再起作用。gconf设置更改了,但背景没有刷新到新的图像。 - hobs
我正在使用11.04版本,刚刚编写了一个脚本来循环遍历文件夹中的图像,并使用了这个代码片段。对我来说运行良好。但是,我正在使用os.system(command)命令来执行该命令。 - MikeVaughan

9
在gnome中,直接使用gconf的python绑定可能更可取:
import gconf
conf = gconf.client_get_default()
conf.set_string('/desktop/gnome/background/picture_filename','/path/to/filename.jpg')

5

首先,导入ctypes: 这将使您能够访问Windows组件,如屏幕保护程序、壁纸等。

然后调用

ctypes.windll.user32.SystemParametersInfoA(20, 0, the_complete_path_of_your_image, 0)

确保路径是您的图像的完整路径,而不仅仅是活动目录中的路径。

参见此链接

3
对于那些感兴趣的人,如果您将此答案与opencv一起使用,您可以在实时中看到自己的墙纸,这太有趣了。source = cv2.VideoCapture(0); path = r"C:\Users\YOUR_NAME_HERE\Desktop\my_face.png"; 然后循环 for _ in range(50): cv2.imwrite(path, source.read()[1]); ctypes.windll.user32.SystemParametersInfoA(20, 0, path, 0); time.sleep(1/10) 最后 source.release() - Guimoute
@Guimoute 这是一个疯狂的想法,但很酷。 - Hacker

5
在Windows上,你需要使用pywin32Windows API进行一些技巧操作。在“Linux”上,答案将取决于运行的桌面环境 - KDE、Gnome或其他更奇特的环境。在KDE(和可能是Gnome)下,你可以使用D-Bus发送消息,通过使用命令行工具dbus-send,你可以在不包括任何新库的情况下执行此操作。
另一个选择是将桌面壁纸设置为文件,然后从Python中编辑/替换该文件 - 但这可能只会在用户登录时才会导致更改。

4

当你在64位或32位操作系统上运行时,调用SystemParametersInfo方法需要使用不同的方式。对于64位系统,你需要使用SystemParametersInfoW(Unicode),而对于32位系统,则需要使用SystemParametersInfoA(ANSI)。

import struct
import ctypes


SPI_SETDESKWALLPAPER = 20
WALLPAPER_PATH = 'C:\\your_file_name.jpg'


def is_64_windows():
    """Find out how many bits is OS. """
    return struct.calcsize('P') * 8 == 64


def get_sys_parameters_info():
    """Based on if this is 32bit or 64bit returns correct version of SystemParametersInfo function. """
    return ctypes.windll.user32.SystemParametersInfoW if is_64_windows() \
        else ctypes.windll.user32.SystemParametersInfoA


def change_wallpaper():
    sys_parameters_info = get_sys_parameters_info()
    r = sys_parameters_info(SPI_SETDESKWALLPAPER, 0, WALLPAPER_PATH, 3)

    # When the SPI_SETDESKWALLPAPER flag is used,
    # SystemParametersInfo returns TRUE
    # unless there is an error (like when the specified file doesn't exist).
    if not r:
        print(ctypes.WinError())


change_wallpaper()

1
这是无稽之谈。选择ASCII或Unicode与您是否运行32位或64位Windows完全无关。(在16位Windows和Windows 95/98/ME上必须使用ASCII,但Windows NT始终支持Unicode,包括32位和64位版本。) - Harry Johnston
@HarryJohnston 那你如何解释SystemParametersInfoA在64位Windows 10上无法工作的问题? - Johnny
1
@Johnny,我刚试了一下,对我来说它完美地运行了。这是来自C的,所以仍然有可能发生一些与Python相关的怪事,这取决于操作系统的位数,但这似乎非常不可能。查看ctypes文档,它应该只取决于您使用的是Python 2还是Python 3。 - Harry Johnston
我觉得有趣的是 Vlad 没有为他的发现辩护。 - Johnny
“is_64_windows()” 实际上是用来确定 Python 安装的是 32 位还是 64 位版本,不是吗?在 64 位 Windows 上,你可以自由地安装 32 位 Python。 - Andris

4
import ctypes,win32con

def getWallpaper():
    ubuf = ctypes.create_unicode_buffer(512)
    ctypes.windll.user32.SystemParametersInfoW(win32con.SPI_GETDESKWALLPAPER,len(ubuf),ubuf,0)
    return ubuf.value

def setWallpaper(path):
    changed = win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE
    ctypes.windll.user32.SystemParametersInfoW(win32con.SPI_SETDESKWALLPAPER,0,path,changed)

或者:(使用SystemParametersInfoA

def getWallpaper():
    sbuf = ctypes.create_string_buffer(512) # ctypes.c_buffer(512)
    ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_GETDESKWALLPAPER,len(sbuf),sbuf,0)
    return sbuf.value

def setWallpaper(path):
    changed = win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE
    ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_SETDESKWALLPAPER,0,path.encode(),changed) # "".encode() = b""

参数为:
SystemParametersInfo(SetOrGet, GetBufferSize, SetBufferOrGetBuffer, SetChange)

路径必须是绝对路径,因此如果您使用相对于脚本的某些内容,则执行以下操作:
path = os.path.abspath(path)

要查看有关使用 SystemParametersInfo 的更多信息,请参见文档
(底部有一个更改鼠标速度的示例)

附言:这里已经有很多答案了,但它们遗漏了您应该执行的广播。当然,不使用它也可以工作,但是不正确地使用它是不好的做法。

另外,他们只提供了硬编码的值,而没有提供它们来自的变量。

还要注意,获取路径时我将缓冲区大小设置为512个字符,只是为了更加安全,因为路径可能超过256个字符。虽然我不认为有人会有那么长的路径。

另外再说一句,我只在Python 3中测试了上述示例,但我不认为SystemParametersInfoA需要在Python 2中使用.encode()。(我相信他们在Python 3中更新了字符串为unicode)SystemParametersInfoW中的字符串可能需要在Python 2中进行转换。


你能帮忙吗?https://stackoverflow.com/questions/65914485/set-windows-wallpaper-fit-span-position-using-python-script - Meet

3

我阅读了所有的答案,在搜索一段时间后,我找到了更简便的解决方案。

安装名为py-wallpaper的模块。

pip install py-wallpaper

导入该模块。

from wallpaper import set_wallpaper, get_wallpaper

使用“set wallpaper”设置壁纸。

set_wallpaper("location/to/image.jpg")

使用get wallpaper获取当前墙纸的路径。

print(get_wallpaper())

感谢您的选择。

它不支持Linux(至少描述是这样说的..) - nsde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接