【Python】一个自动切换壁纸的python程序

一个自动切换壁纸的python程序

豆包AI写的,想显示图标需要在项目目录下放置图片img.png

可以使用pyinstaller打包成单文件程序pyinstaller -F -w -i img.png wallpaper.py


python 复制代码
import tkinter as tk
from tkinter import filedialog, messagebox
import ctypes
import os
import sys
import time
import threading
import random
from pathlib import Path
import json
import pystray
from PIL import Image
import winreg

# Windows设置壁纸API
SPI_SETDESKWALLPAPER = 0x0014
SPIF_UPDATEINIFILE = 0x01
SPIF_SENDCHANGE = 0x02

# 全局控制变量
is_running = False
thread = None
img_list = []
current_idx = 0
tray_icon = None


def set_wallpaper(image_path):
    if not os.path.exists(image_path):
        return False
    ctypes.windll.user32.SystemParametersInfoW(
        SPI_SETDESKWALLPAPER, 0, image_path,
        SPIF_UPDATEINIFILE | SPIF_SENDCHANGE
    )
    return True


def load_img_folder(folder_path):
    global img_list, current_idx
    img_suffix = [".jpg", ".jpeg", ".png", ".bmp"]
    img_list.clear()
    current_idx = 0
    if not os.path.isdir(folder_path):
        return False
    for file in os.listdir(folder_path):
        suf = Path(file).suffix.lower()
        if suf in img_suffix:
            img_list.append(os.path.join(folder_path, file))
    return len(img_list) > 0


def loop_change_wallpaper(folder, interval, is_random):
    global is_running, current_idx
    if not load_img_folder(folder):
        messagebox.showerror("错误", "文件夹内没有找到图片!")
        is_running = False
        return

    while is_running:
        if is_random:
            target_img = random.choice(img_list)
        else:
            if current_idx >= len(img_list):
                current_idx = 0
            target_img = img_list[current_idx]
            current_idx += 1
        set_wallpaper(target_img)
        time.sleep(interval)


def select_folder():
    path = filedialog.askdirectory(title="选择壁纸图片文件夹")
    if path:
        entry_folder.delete(0, tk.END)
        entry_folder.insert(0, path)


def start_task():
    global is_running, thread
    if is_running:
        messagebox.showinfo("提示", "已经在运行中!")
        return

    folder = entry_folder.get().strip()
    time_str = entry_time.get().strip()
    random_mode = random_var.get()

    with open('config.json', 'w', encoding='utf-8') as f:
        json.dump({
            "folder": folder,
            "time_str": time_str,
            "random": random_mode
        }, f)

    if not folder or not os.path.isdir(folder):
        messagebox.showerror("错误", "请选择有效的图片文件夹!")
        return
    try:
        interval = int(time_str)
        if interval < 1:
            messagebox.showerror("错误", "间隔时间必须大于0秒!")
            return
    except ValueError:
        messagebox.showerror("错误", "请输入合法数字!")
        return

    is_running = True
    thread = threading.Thread(target=loop_change_wallpaper, args=(folder, interval, random_mode))
    thread.daemon = True
    thread.start()
    label_status.config(text="状态:运行中", fg="green")


def stop_task():
    global is_running
    is_running = False
    label_status.config(text="状态:已停止", fg="red")


# ==================== 开机自启功能 ====================
def set_startup(enable):
    key = winreg.HKEY_CURRENT_USER
    key_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
    app_name = "自动壁纸轮换工具"
    exe_path = os.path.abspath(sys.argv[0])

    try:
        with winreg.OpenKey(key, key_path, 0, winreg.KEY_WRITE) as reg:
            if enable:
                winreg.SetValueEx(reg, app_name, 0, winreg.REG_SZ, exe_path)
                messagebox.showinfo("成功", "✅ 已设置开机自动启动")
            else:
                try:
                    winreg.DeleteValue(reg, app_name)
                    messagebox.showinfo("成功", "❌ 已取消开机自动启动")
                except FileNotFoundError:
                    messagebox.showinfo("提示", "未设置过开机启动")
    except Exception as e:
        messagebox.showerror("错误", f"设置失败:{str(e)}")


# ==================== 托盘图标功能 ====================
def show_window():
    root.deiconify()
    root.lift()
    root.focus_force()


def quit_app():
    stop_task()
    if tray_icon:
        tray_icon.stop()
    os._exit(0)


def next_wallpaper():
    global current_idx
    folder = entry_folder.get().strip()
    random_mode = random_var.get()

    if not img_list:
        load_img_folder(folder)

    if img_list:
        if random_mode:
            target = random.choice(img_list)
        else:
            current_idx = (current_idx + 1) % len(img_list)
            target = img_list[current_idx]
        set_wallpaper(target)


def load_tray_icon():
    try:
        return Image.open("img.png")
    except:
        return Image.new("RGB", (32, 32), (70, 70, 70))


def setup_tray():
    global tray_icon
    icon_img = load_tray_icon()

    menu = pystray.Menu(
        pystray.MenuItem("显示主界面", show_window),
        pystray.MenuItem("立即切换", next_wallpaper),
        pystray.MenuItem("退出", quit_app)
    )

    tray_icon = pystray.Icon("WallpaperTool", icon_img, "自动壁纸轮换", menu=menu)
    tray_icon.on_left_click = lambda _, __: show_window()
    tray_icon.run()


def hide_to_tray():
    root.withdraw()


# ==================== 配置 & 界面 ====================
if os.path.exists('config.json'):
    with open('config.json', 'r', encoding='utf-8') as f:
        config = json.load(f)
else:
    config = {"folder": "", "time_str": '30', "random": False}

root = tk.Tk()
root.title("自动壁纸轮换工具")
root.geometry("450x320")
root.resizable(False, False)

root.protocol("WM_DELETE_WINDOW", hide_to_tray)
root.bind("<Unmap>", lambda e: hide_to_tray() if root.state() == "iconic" else None)

# 界面布局
tk.Label(root, text="图片文件夹:").place(x=20, y=30)
entry_folder = tk.Entry(root, width=30)
entry_folder.place(x=110, y=30)
entry_folder.insert(0, config.get("folder", ""))
tk.Button(root, text="选择目录", command=select_folder).place(x=360, y=28)

tk.Label(root, text="轮换间隔(秒):").place(x=20, y=80)
entry_time = tk.Entry(root, width=30)
entry_time.place(x=110, y=80)
entry_time.insert(0, config.get("time_str", "30"))

tk.Label(root, text="轮播模式:").place(x=20, y=130)
random_var = tk.BooleanVar(value=config.get("random", False))
tk.Radiobutton(root, text="顺序轮播", variable=random_var, value=False).place(x=110, y=130)
tk.Radiobutton(root, text="随机轮播", variable=random_var, value=True).place(x=220, y=130)

tk.Button(root, text="开始轮换", width=12, height=2, command=start_task).place(x=80, y=180)
tk.Button(root, text="停止轮换", width=12, height=2, command=stop_task).place(x=220, y=180)

# 开机自启按钮
tk.Button(root, text="开机自动启动", width=12, height=1, command=lambda: set_startup(True)).place(x=80, y=230)
tk.Button(root, text="取消开机启动", width=12, height=1, command=lambda: set_startup(False)).place(x=220, y=230)

label_status = tk.Label(root, text="状态:未启动", fg="gray")
label_status.place(x=180, y=290)

# 启动托盘
tray_thread = threading.Thread(target=setup_tray, daemon=True)
tray_thread.start()

root.mainloop()
相关推荐
2501_941875281 小时前
从配置中心到动态管理的互联网工程语法演进与多语言实践分享
开发语言·python
hfywmsj1 小时前
广州餐饮铺位招租决策模型:多因子选址系统设计
开发语言·人工智能·python·广州餐饮铺位招租
努力的lpp3 小时前
php反序列化一(大白话版)
开发语言·web安全·php·ctf·反序列化
菜鸟~noob2334 小时前
【电子战】第03篇:CFAR(恒虚警)处理【含matlab代码】
开发语言·matlab
泡海椒4 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
hqyjzsb5 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
sanjiaomao3335 小时前
从论文公式到Python实现:用单元测试校验滑动平均算法
python·算法·单元测试
Keven_115 小时前
算法札记:ACM适用的很骚的C++语法(持续更新)
开发语言·c++
脉动数据行情16 小时前
C# 实现德指 DAX 期货 WebSocket 实时行情监听
开发语言·websocket·c#
大圣编蚕6 小时前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法