3

I am changing my windows desktop background with the following code

ctypes.windll.user32.SystemParametersInfoW(20, 0, "C:/image/jkk7LGN03aY.jpg" , 0)

my image directory has so many images and I am setting those one by one as follows

for path in image_list:
    ctypes.windll.user32.SystemParametersInfoW(20, 0, path , 0)
    time.sleep(5)

Desktop background image is changing abruptly but I want a smooth transition. How can I do this?

Samual
  • 512
  • 6
  • 19

1 Answers1

4

Here's a pure Python snippet that I use regularly:

It uses pywin32 to enable active desktop and set the wallpaper using a smooth transition (make sure you've not disabled window effects or you won't see any fade effect)

import ctypes
from typing import List

import pythoncom
import pywintypes
import win32gui
from win32com.shell import shell, shellcon

user32 = ctypes.windll.user32


def _make_filter(class_name: str, title: str):
    """https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows"""

    def enum_windows(handle: int, h_list: list):
        if not (class_name or title):
            h_list.append(handle)
        if class_name and class_name not in win32gui.GetClassName(handle):
            return True  # continue enumeration
        if title and title not in win32gui.GetWindowText(handle):
            return True  # continue enumeration
        h_list.append(handle)

    return enum_windows


def find_window_handles(parent: int = None, window_class: str = None, title: str = None) -> List[int]:
    cb = _make_filter(window_class, title)
    try:
        handle_list = []
        if parent:
            win32gui.EnumChildWindows(parent, cb, handle_list)
        else:
            win32gui.EnumWindows(cb, handle_list)
        return handle_list
    except pywintypes.error:
        return []


def force_refresh():
    user32.UpdatePerUserSystemParameters(1)


def enable_activedesktop():
    """https://stackoverflow.com/a/16351170"""
    try:
        progman = find_window_handles(window_class='Progman')[0]
        cryptic_params = (0x52c, 0, 0, 0, 500, None)
        user32.SendMessageTimeoutW(progman, *cryptic_params)
    except IndexError as e:
        raise WindowsError('Cannot enable Active Desktop') from e


def set_wallpaper(image_path: str, use_activedesktop: bool = True):
    if use_activedesktop:
        enable_activedesktop()
    pythoncom.CoInitialize()
    iad = pythoncom.CoCreateInstance(shell.CLSID_ActiveDesktop,
                                     None,
                                     pythoncom.CLSCTX_INPROC_SERVER,
                                     shell.IID_IActiveDesktop)
    iad.SetWallpaper(str(image_path), 0)
    iad.ApplyChanges(shellcon.AD_APPLY_ALL)
    force_refresh()


if __name__ == '__main__':
    set_wallpaper(r'D:\Wallpapers\Cool\enchanted_mountain_4k.jpg')

abdusco
  • 9,700
  • 2
  • 27
  • 44
  • 1
    Tested it and it is running like a charm. Thank you so much. – Samual Jul 10 '19 at 22:09
  • When I use this script, smaller images are tiled instead of scaled to fill the desktop. Is that something that can be changed by sending a different parameter, or do I have to handle resizing the image myself, before sending the path to "setWallpaper()"? – Matt Oct 18 '19 at 03:55
  • 1
    Change the wallpaper fill style from Settings > Personalization > Background > Choose a fit > Fill – abdusco Oct 18 '19 at 05:07
  • That worked. It's a little disappointing. I'd rather set that within my script, but that's probably more complicated than it's really worth. – Matt Oct 23 '19 at 03:10
  • Now I'm having a problem where the new image fades in and then briefly flashes back to the previous image before settling on the new image. Anyone solve that problem before? None of the other desktop changers have this problem, so I don't think it's a performance/hardware issue. – Matt Oct 23 '19 at 03:14
  • It's possible using Python too, but I have to translate C# code into Python equivalent. But without proper typing support it's difficult to write and test Win32 API code. – abdusco Oct 23 '19 at 06:04
  • Are you changing wallpapers too quickly? That might be the reason for the flicker. – abdusco Oct 23 '19 at 06:04
  • No, it's only every 5 minutes. After looking at it more, I actually think it's because the refresh rate between my primary monitor and my additional monitor. – Matt Oct 23 '19 at 12:03
  • I can't find the package `win32com.shell`. Any pointers to where I could find it? – Safron May 04 '20 at 18:00
  • 1
    @Safron install pywin32 library – abdusco May 05 '20 at 23:26
  • 1
    For me, Windows Explorer gradually slows down when using this method (dragging windows becomes laggy, alt+tab becomes slow, etc). Restarting Windows Explorer fixes it temporarily. Is this code leaving previous images loaded? Could it possibly contain a memory leak of some sort? – Emerson May 23 '21 at 02:50
  • The script runs and python process terminates. The resources it's allocated are released by the operating system. It's not possible to for it to have a memory leak. If you're calling this script in a long-running script, try checking memory usage from the task manager. – abdusco May 23 '21 at 06:07
  • This works, but during a transition my whole pc slows down (my mouse and keyboard hang, the task bar flickers, minimizing/maximizing windows block). Any idea how this code can cause this? – Safron Jul 23 '21 at 12:54
  • Correction: the problem occurs for a couple of seconds right after the transition. Could it be that Windows performs some post processing on the wallpaper? – Safron Jul 23 '21 at 13:15
  • 1
    Two things I did to boost performance. (1) Only enable active desktop once and reuse the 'iad' instance. (2) Disable the "Automatically pick an accent color from my background" in Windows settings. – Safron Jul 23 '21 at 13:47