I am making an app that changes the users wallpaper when ran. One feature i made is copying the file to startup. When i run the file in VScode. The file gets deleted, copies itself to startup, and works when ran from startup, however when I convert the file to an exe using this command:
pyinstaller --noconfirm --onefile --console --ascii --clean "E:/Path/to/file"
it doesnt work.Here is the code:
import os
import requests
import ctypes
from plyer import notification
import shutil
user_home = os.path.expanduser('~')
startfolder = os.path.join(user_home, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
script_file = os.path.basename(__file__)
startup_script_path = os.path.join(startfolder, script_file)
def copy_to_startup():
if not os.path.exists(startup_script_path):
shutil.copy2(__file__, startfolder)
def notify():
notification.notify(
title='Wallpaper Change',
message='Your Wallpaper has been changed!',
timeout=3,
toast=True
)
def wallpaper():
SPI_SETDESKWALLPAPER = 0x0014
SPIF_UPDATEINIFILE = 0x01
SPIF_SENDCHANGE = 0x02
image_url = 'https://picsum.photos/1920/1080'
response = requests.get(image_url)
image_data = response.content
local_path = os.path.join(startfolder, 'wallpaper.png')
with open(local_path, 'wb') as f:
f.write(image_data)
result = ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, local_path, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)
notify()
return result
if not os.path.realpath(__file__) == startup_script_path:
copy_to_startup()
os.remove(os.path.realpath(__file__))
input()
elif os.path.realpath(__file__) == startup_script_path:
wallpaper()
input()
I removed the last if statement (line 39-line 44) and replaced it with wallpaper() and the exe works and changes the wallpaper. Why doesn't the converted exe work?
The issue you're facing arises due to how pyinstaller bundles the Python script into an executable. When you run the compiled .exe, file points to a temporary directory created by pyinstaller, not the actual .exe file location.
This causes the script not to copy itself correctly to the startup folder when run as an executable. You can resolve this by replacing file with sys.executable, which points to the Python interpreter executable. In the case of a pyinstaller bundle, it points to the .exe file itself.
import sys
...
script_file = os.path.basename(sys.executable)
...
def copy_to_startup():
if not os.path.exists(startup_script_path):
shutil.copy2(sys.executable, startfolder)
...
if not os.path.realpath(sys.executable) == startup_script_path:
copy_to_startup()
os.remove(os.path.realpath(sys.executable))
input()
elif os.path.realpath(sys.executable) == startup_script_path:
wallpaper()
input()