【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()
相关推荐
http阿拉丁神猫10 小时前
Go语言入门笔记:从基础到开发环境搭建(二)
开发语言·笔记·golang
不会就选b11 小时前
C++补充部分(一)--lambda 表达式
开发语言·c++
4SAPI13 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
Pniubi14 小时前
HashMap的扩容和树化边界
java·开发语言
XCDoris15 小时前
郴州logo设计机构
大数据·人工智能·python
gCode Teacher 格码致知16 小时前
Pandas教学-17:Series与Dataframe的关系
开发语言·python·pandas
发量惊人的中年网工16 小时前
分公司访问总部ERP慢怎么办?SD-WAN组网的排查与验收要点
服务器·开发语言·php
下页、再停留16 小时前
【C#桌面客户端系列学习-15】生成可视化图表之折线图、柱状图、饼状图
开发语言·c#
ShineWinsu16 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
马良神笔17 小时前
2026 年Rag最近技术前沿总结
后端·python·django