【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()
相关推荐
跨境数据猎手2 小时前
B 站 item_search_video 接口开发,搭建生产级视频搜索服务
大数据·爬虫·python
ShiJiuD6668889992 小时前
JSP Cookie和Session
java·开发语言
EnCi Zheng2 小时前
03ab-PyTorch安装教程 [特殊字符]
人工智能·pytorch·python
SmartBrain2 小时前
从Prompt工程到Harness工程:AI Agent落地之路
人工智能·python·华为·aigc
X566110 小时前
如何在 Laravel 中正确保存嵌套动态表单数据(主服务与子服务)
jvm·数据库·python
FQNmxDG4S10 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
ZhengEnCi10 小时前
03ab-PyTorch安装教程 📚
python
前端老石人10 小时前
HTML 字符引用完全指南
开发语言·前端·html
matlab_xiaowang11 小时前
Redux 入门:JavaScript 可预测状态管理库
开发语言·javascript·其他·ecmascript