【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()
相关推荐
码兄科技7 小时前
Java AI智能体开发实战:从零构建智能对话系统指南
java·开发语言·人工智能
Drone_xjw8 小时前
从 GDB 到 CDB:C/C++ 程序调试的两把“手术刀”
c语言·开发语言·c++
SL-staff9 小时前
智慧园区2000+设备统一管理:JVS-IoT如何降低运维成本40%
java·开发语言·物联网·智慧园区·设备管理·运维优化·jvs-iot
聪明的一休丶9 小时前
VLLM v0.24.0 版本深度解析:新引擎、新架构与大规模服务全家桶升级
python·架构·vllm
2zcode10 小时前
基于MATLAB图像处理的饮料瓶灌装液位检测系统设计与实现
开发语言·图像处理·matlab
万亿少女的梦16810 小时前
基于Python的高考志愿填报辅助系统设计与实现
java·spring boot·python·mysql·vue
必须得开心呀11 小时前
QT解决中文乱码问题
开发语言·qt
闲猫12 小时前
Python FastAPI + SQLAlchemy 入门教程:从零搭建你的第一个 Web 应用
前端·python·fastapi
这不小天嘛12 小时前
JAVA八股——redis篇
java·开发语言·redis