Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on How to programmatically get current wallpaper file path?

Parent

How to programmatically get current wallpaper file path?

+4
−0

I have written a screensaver program that, in part, uses the current wallpaper for some visual effects. However, finding the current wallpaper in different operating systems and environments is less than straightforward.

I've made some progress, but what I've found during searches has not been very helpful[1]. So, how can I find the file system path to the current wallpaper in the following OS/environments?

GNU+Linux

  1. KDE/Plasma
  2. Gnome
  3. Unity (TBD)
  4. etc?

Microsoft

  1. Win 7
  2. Win 10
  3. Win 11 (TBD)

Apple

  1. MacOS (TBD)
  2. ?

Remarks

Please note that I don't have direct access to all of the above (e.g. no Windows 11, no Unity, no Apple systems), but I'd like this program to work on those OS/environments if a user were to run it there, so answers that have been tested to work prior to posting would be greatly appreciated.

Also, I'm not sure of what the best way to make/add a canonical answer here is, but I'd like to try that here with my answer by accumulating what has been tested/verified to work.


  1. It includes abandoned libraries that no longer work, explicitly state they don't support certain OS/environments, and out-of-date recommendations and/or commands that don't (seem to?) work anymore. For example, the command gsettings get org.gnome.desktop.background picture-uri has showed up multiple times, but in Gnome, it does not give me the path to the current wallpaper; it just points to another image. ↩︎

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

Post
+3
−0

This answer will use Python 3 for code examples. I encourage people to post other answers for other languages. I'll continue to update this answer, as I'm able, based on comments with tested/verified recommendations/code/etc.

Checking OS

import platform

sys = platform.system().lower()
if sys == 'linux':
    # do Linux stuff here
elif sys == 'windows':
    # do Windows stuff here
else:
    # ...

GNU+Linux

To identify which desktop environment is being used, I'm using the DESKTOP_SESSION environment variable. I get the following string values:

  1. KDE/Plasma: plasma
  2. Gnome: ubuntu
  3. Unity: (TBD)

If there're other synonyms and/or strings for different versions of the same environment, that'd also be good to know so it can be added above.

Code:

from os import environ as env

kde = ('plasma',)
gnome = ('ubuntu',)
session = env['DESKTOP_SESSION']

if session in kde:
    # KDE/Plasma implementation here
elif session in gnome:
    # Gnome implementation here
else:
    # ...

NOTE: The GNU+Linux distro being used here is Ubuntu 22.10.

1. KDE/Plasma

Find the following file/line:

  1. File: ~/.config/plasma-org.kde.plasma.desktop-appletsrc
  2. Line: Image=file://<abs-path>

Note that:

  1. <abs-path> is the absolute path to the file in the file system (e.g. /home/<you>/Pictures/your-wallpaper.jpg);
  2. you may have multiple Image= entries (e.g. due to multiple plasma activities; I have 2 activities and 2 entries; entry 1 is for activity 1, etc.);
  3. we're only grabbing the 1st entry, which is the wallpaper for the 1st activity;
  4. you can look at the kscreenlockerrc file if interested in the image used by the screen locker.

Code:

from os.path import join, expanduser

wallpaper_path = None
configpath = join(
    expanduser('~'),
    '.config',
    'plasma-org.kde.plasma.desktop-appletsrc'
)
with open(configpath) as config:
    for line in config.readlines():
        if line.startswith('Image='):
            wallpaper_path = line.split('=')[1].removeprefix('file://').strip()
            break

2. Gnome

The process under Gnome boils down to the following steps:

  1. Find out what the user's theme is, and
  2. Use the theme-specific command to get the wallpaper path.

Theme

On a clean installation (e.g. Ubuntu 22.10), the user has the following theme options:

  1. Default
  2. Dark

Use this command to find out the theme currently set by the user:

gsettings get org.gnome.desktop.interface color-scheme

The possible results are below:

Theme Value
Default 'default'
Dark 'prefer-dark'

You can use the subprocess module for this:

theme = subprocess.run(
    'gsettings get org.gnome.desktop.interface color-scheme',
    capture_output=True,
    shell=True,
    universal_newlines=True
).stdout.strip().replace("'", '')

The returned string must have newlines and extra ' characters removed, hence the strip and replace calls.

Path

The command to get the wallpaper path is:

gsettings get org.gnome.desktop.background <key>

where <key> is one of the following theme-dependent values:

Theme Key
default picture-uri
prefer-dark picture-uri-dark

NOTE: Correct key selection depends on the result from the previous section. Using the wrong key will still give you a file system path. However, that path is not necessarily the current wallpaper (you could get lucky). In fact, the path may not even point to an existing file (e.g. running the command under KDE).

For example, if the user is using the Dark theme, then the correct command to get the path is:

gsettings get org.gnome.desktop.background picture-uri-dark

This is the reason the commands I had found hadn't been helpful, because I'm always using the dark theme, but the info/commands posted made no mention of these details.

Assuming the theme variable from the previous section, you can, again, use the subprocess module:

theme_key = 'picture-uri-dark' if theme == 'prefer-dark' else 'picture-uri'
wallpaper_path = subprocess.run(
    f'gsettings get org.gnome.desktop.background {theme_key}',
    capture_output=True,
    shell=True,
    universal_newlines=True
).stdout.strip().replace("'", '').removeprefix('file://')

3. Unity (TBD)

Microsoft

In Windows, there're at least two ways to go about this:

  1. Using a PowerShell command, such as Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\Control Panel\Desktop" -Name Wallpaper via the subprocess module;
  2. Using the built-in winreg module, which is part of the standard library (though only available in Windows).

I'm recommending Python's winreg for the following reasons:

  1. The implementation is more consistent across different versions of Windows;
  2. The PowerShell command Get-ItemPropertyValue is not implemented in Windows 7;
  3. It should perform better anyway, since it doesn't need to spawn a new process.

Windows 7/10

Code for Windows:

import winreg

hkey = winreg.HKEY_CURRENT_USER
subkey = 'Control Panel\\Desktop'
wallpaper_path = None
with winreg.OpenKeyEx(hkey, subkey) as regkey:          # type: PyHKEY
    value = winreg.QueryValueEx(regkey, 'Wallpaper')    # type: Tuple[str, int]
    wallpaper_path = value[0]
    try:
        with open(wallpaper_path): pass                 # see caveats below
    except Exception:
        # good luck

The structure returned by the QueryValueEx function is a tuple that looks like this:

('C:\\The\\Path To\\Your\\Wallpaper\\Image.jpg', 1)

We're interested in the first tuple element, hence value[0].

Caveats

The above code has the following version-dependent caveats:

  1. Windows 7 (Python 3.7):
    1. Python 3.7 is the most recent version that will work in this Windows version, so this is how it was tested.
    2. When the user sets the wallpaper image, the OS seems to copy the file to a pre-determined location.
    3. This location seems to always remain valid, regardless of subsequent user actions.
  2. Windows 10 (Python 3.11):
    1. When the user sets the wallpaper image, the OS simply stores the path to that image at the time it was set. (The OS does not seem to copy the original image to any other location like Win7 does, which is bad for us.)
    2. Subsequent user actions on the original image (e.g. rename, delete, etc) will invalidate the registry value, which still points to the original location.
    3. This implies the path returned by QueryValueEx may not even exist at the time we query for it, which means you need a fallback.
    4. We use open(wallpaper_path) as a safe way to verify the path is valid and can be accessed.
    5. The PowerShell command returns the same path as QueryValueEx, which means the PowerShell code may be using the same function internally.

Windows 11 (TBD?)

The code above might work, but I don't have Windows 11 and cannot test it here.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

2 comment threads

Use `os.path.exists` instead of open (2 comments)
Windows 10 Enterprise, Home, and Win32 API (9 comments)
Use `os.path.exists` instead of open
MattDMo‭ wrote over 1 year ago

Instead of using open() to check if the wallpaper file exists, use os.path.exists instead. This way, you're simply reading a boolean value instead of potentially dealing with an exception.

ghost-in-the-zsh‭ wrote over 1 year ago · edited over 1 year ago

MattDMo‭ I think using open makes more sense b/c it implicitly checks for your ability to read the file, which is necessary for the program to work correctly, among other things.

For example, in GNU+Linux, you could remove all permissions from the wallpaper file, including read, and os.path.exists will still return True. (YMMV. The docs say that it may return False in some cases even if it actually exists, but the system is unable to stat the file.)

Using open not only guarantees the file exists, but also that you're able to read it, and that it's not "locked" in some way by some other process --i.e. you're actually able to use the file.