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.
How to programmatically get current wallpaper file path?
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
- KDE/Plasma
- Gnome
- Unity (TBD)
- etc?
Microsoft
- Win 7
- Win 10
- Win 11 (TBD)
Apple
- MacOS (TBD)
- ?
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.
-
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. ↩︎
1 answer
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:
- KDE/Plasma:
plasma
- Gnome:
ubuntu
- 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:
-
File:
~/.config/plasma-org.kde.plasma.desktop-appletsrc
-
Line:
Image=file://<abs-path>
Note that:
-
<abs-path>
is the absolute path to the file in the file system (e.g./home/<you>/Pictures/your-wallpaper.jpg
); - 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.); - we're only grabbing the 1st entry, which is the wallpaper for the 1st activity;
- 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:
- Find out what the user's theme is, and
- 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:
- Default
- 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:
- Using a PowerShell command, such as
Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\Control Panel\Desktop" -Name Wallpaper
via thesubprocess
module; - 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:
- The implementation is more consistent across different versions of Windows;
- The PowerShell command
Get-ItemPropertyValue
is not implemented in Windows 7; - 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:
-
Windows 7 (Python 3.7):
- Python 3.7 is the most recent version that will work in this Windows version, so this is how it was tested.
- When the user sets the wallpaper image, the OS seems to copy the file to a pre-determined location.
- This location seems to always remain valid, regardless of subsequent user actions.
-
Windows 10 (Python 3.11):
- 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.)
- Subsequent user actions on the original image (e.g. rename, delete, etc) will invalidate the registry value, which still points to the original location.
- This implies the path returned by
QueryValueEx
may not even exist at the time we query for it, which means you need a fallback. - We use
open(wallpaper_path)
as a safe way to verify the path is valid and can be accessed. - 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.
0 comment threads