【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()
相关推荐
rrrjqy几秒前
夏普比率与 Beta——Quant-for-Beginners 量化入门Task7
python·金融
减瓦7 分钟前
深入 Quarkus:云原生时代 Java 的重生之路
java·开发语言·云原生
杨运交18 分钟前
[053][核心模块]Java枚举缓存与ORM集成实践
java·开发语言·缓存
2401_894915531 小时前
Geo优化系统源码部署搭建技术——PHP程序开发部署指南
开发语言·php
wuyk5551 小时前
67.嵌入式C语言进阶:结构体指针实战指南——STM32外设、传感器数据访问的高效技巧
c语言·开发语言·stm32·单片机
找磁材1 小时前
空心杯电机与永磁体
开发语言·磁性材料·钕铁硼·磁钢
我不管我就要叫小猪1 小时前
C/C++----命名空间
c语言·开发语言·c++
min(a,b)1 小时前
学习第 6 天:类型注解、装饰器与高级特性
python·学习
兮动人2 小时前
Python字符串类型
开发语言·python·python字符串类型
自由随风飘2 小时前
JAVA中的面向对象-3
java·开发语言