Python实战:病毒困我于斗室,桌面带我去旅行

近代史上,每一个庚子年都不寻常:1840年鸦片战争,1900年八国联军侵华,1960年大饥荒。2020年的这个春节,一场突如其来的新型冠状病毒疫情横扫东亚,大有席卷全球之势。这场无妄之灾,使得很多人放弃了聚会和旅行的计划,大家不得不蜗居于斗室。但是,病毒再猖狂,也无法阻止程序员对于美好的生活的向往。今天,我来教大家用Python做一个壁纸切换工具,展示以前旅行的照片,让桌面带我们去周游世界!

1. 用Python设置Windows壁纸

设置Windnows壁纸,需要修改Windows注册表,用Python操作Windows注册表,一般使用Pywin32模块。该模块包装了几乎所有的Windows API,可以方便地从Python直接调用。Pywin32允许你像VC一样的形式来使用Python开发win32应用,代码风格可以类似win32 sdk,也可以类似MFC,由你选择。安装Pywin32模块,可以使用如下命令直接安装,也可以从Python模块仓库下载whl文件在本地安装。

python -m pip install pywin32

用Python设置Windows壁纸,分为5步,核心代码只有5行:

import win32api
import win32gui
import win32con

def set_wallpaper(photo_path):
    """设置壁纸"""

    # 1.打开注册表键
    key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)

    # 2.设置壁纸风格:0=居中 1=平铺 2=拉伸
    win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, "2") 

    # 3.设置壁纸是否缩放:0=缩放 1=原图
    win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, "0")

    # 4.设置壁纸
    win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, photo_path, 3)

    # 5.关闭注册表键
    win32api.RegCloseKey(key)

随便找了一张去年春天济南五龙潭樱花盛开时节拍摄的照片,运行:

photo_path = r'D:\CSDN\Column\desktop\album\20200129150646.jpg'
set_wallpaper(photo_path)

我的桌面瞬间变成了这样:
在这里插入图片描述

熟悉注册表的同学可以跳过本节剩余内容,直接进入下一节。对注册表结构感兴趣的同学,可以继续读完本节。点击Win + R,输入regedit可以打开系统的注册表编辑器,如下图所示:
在这里插入图片描述

注册表分为左右两部分,左侧是键目录,右侧是值列表。注册表中键和值,与我们程序员通常所说的键和值不同。这里的键类似于目录,值类似于文件,每个值都包含了三项内容:名称、类型、数据。

Windows的注册表的键一个树状结构,它有六大根节点:

  • HKEY_LOCAL_MACHINE:注册表的核心主键,记录了计算机上的所有硬件和软件的配置
  • HKEY_CLASSES_ROOT:这其实不是真正的要节点,而是HKEY_LOCAL_MACHINE\Software\Classes的链接,对该节点的操作与操作HKEY_LOCAL_MACHINE\Software\Classes是一样的
  • HKEY_USERS:注册表的核心主键,记录了Windows中所有用户的设置信息
  • HKEY_CURRENT_USER:是HKEY_USERS中当前用户键的链接,保存了当前登录用户的所有配置信息
  • HKEY_CURRENT_CONFIG:是HKEY_LOCAL_MACHINE\Config的链接
  • HKEY_DYN_DATA:记录了系统的动态数据

壁纸信息每个用户是不同的,所以我们需要操作HKEY_CURRENT_USER中的内容,具体是HKEY_CURRENT_USER\Control Panel\Desktop,这里面有三个值修改修改:

  • WallPaper:壁纸文件的路径
  • WallpaperStyle:有三个选项:0=居中 1=平铺 2=拉伸
  • TileWallpaper:有两个选项:0=缩放 1=原图

使用pywin32模块操作注册表,需要进行三步操作:

  1. 打开注册表键
    调用win32api.RegOpenKeyEx打开注册表键,它有四个参数,分别为:
    • 根节点
    • 子节点
    • 必须为0
    • 对打开的子项进行的操作,包括win32con.KEY_ALL_ACCESS、win32con.KEY_READ、win32con.KEY_WRITE、win32con.KEY_SET_VALUE等
  2. 修改注册表值
    要修改注册表的值,在打开键时,第四个参数需要设置为win32con.KEY_SET_VALUE,然后可以调用win32api.RegSetValueEx修改值,它有五个参数为:
    • 打开的注册表键
    • 值的名称
    • 必须为0
    • 值的数据类型,包括win32con.REG_BINARY、win32con.REG_SZ、win32con.REG_DWORD等
  3. 关闭注册表键
    操作完注册表后,可以调用win32api.RegCloseKey关闭注册表键

2. 随机切换Windows壁纸

接下来,我们将旅行照片整理到一个目录中,递归遍历所有的照片,然后随机选择一张设置为壁纸:

# -*- coding: utf-8 -*-

import os, random
import win32api, win32gui, win32con

def set_wallpaper(photo_path):
    """设置壁纸"""

    # 1.打开注册表键
    key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)

    # 2.设置壁纸风格:0=居中 1=平铺 2=拉伸
    win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, "2") 

    # 3.设置壁纸是否缩放:0=缩放 1=原图
    win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, "0")

    # 4.设置壁纸
    win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, photo_path, 3)

    # 5.关闭注册表键
    win32api.RegCloseKey(key)
    
def set_wallpaper_random(photo_dir):
    """随机设置壁纸"""

    wall_papers = list()
    for root, dirs, files in os.walk(photo_dir):
        for name in files:
            if os.path.splitext(name)[1].lower() == ".jpg":
                wall_papers.append(os.path.join(root, name))
    
    set_wallpaper(random.choice(wall_papers))
    
if __name__ == '__main__':
    #photo_path = r'D:\CSDN\Column\desktop\album\20200129150646.jpg'
    #set_wallpaper(photo_path)
    set_wallpaper_random(r'D:\CSDN\Column\desktop\album')

这个代码运行一次,就可以随机切换一张壁纸。我运行了4次,4个桌面效果如下:
在这里插入图片描述

3. 定时切换Windows壁纸

每次想改变壁纸的时候,都得需要手动运行上面的代码,感觉有点麻烦。那好,下面我们就做一个自动定时切换的。实现定时切换功能,需要一个定时调度服务框架。前几天我在数据抓取服务中刚刚介绍了APScheduler库,今天我们继续用它来实现任务的定时执行。APScheduler安装命令如下:

python -m pip install apscheduler

安装完成后可以先用下面的代码试验效果:

from apscheduler.schedulers.blocking import BlockingScheduler

# 任务执行函数
def do_job():
    print("do_job")

# 创建调度器
scheduler = BlockingScheduler()

# 添加job
scheduler.add_job(do_job,               # 时间到后的执行函数
                trigger="cron",         # 设置触发器为cron     
                second="*/1",           # 设置每隔一秒执行一次
                misfire_grace_time=60   # 如果任务较多,60秒内没有来得急执行此job时,放弃执行
                )

# 启动调度                      
scheduler.start()

如果可以每秒打印出do_job,说明apscheduler可以正常执行。apscheduler有很多种触发器,在这段代码中,使用了cron触发器,这是apscheduler中最为复杂的触发器,它可以使用cron语法,指定非常复杂的触发方式。

cron语法包含秒、分钟、小时、日、月、星期、年七个域,每个域中可以使用一些通配符,用来匹配任务可以被执行的时间,当7个域都被匹配成功时,任务将被执行。每个域支持的通配符如下:

  • 秒:可使用", - * /"四个字符以及有效范围为0-59的整数
  • 分钟:可使用", - * /"四个字符以及有效范围为0-59的整数
  • 小时:可出现", - * /"四个字符以及有效范围为0-23的整数
  • 日:可使用", - * / ? L W C"八个字符以及有效范围为0-31的整数
  • 月:可使用", - * /"四个字符、有效范围为1-12的整数或JAN-DEC
  • 星期:可使用", - * / ? L C #"四个字符、有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
  • 年:可使用", - * /"四个字符、有效范围为1970-2099年

其中,各字符的含义如下:

  • , 表示列出枚举值值。

    在“分钟”域使用5,20,则意味着在5和20分触发两次
    
  • - 表示范围.

    在“分钟”域使用5-20,表示从5分到20分钟每分钟触发一次
    
  • * 表示匹配该域的任意值。

    在“分钟”域使用*, 即表示每分钟都会触发事件
    
  • / 表示起始时间开始触发,然后每隔固定时间触发一次.

    在“分钟”域使用5/20,则意味着5分钟触发一次,以后间隔20分钟触发一次,即5、25、45等分别触发一次
    
  • ? 也匹配域的任意值,但只能用在“日”和“星期”两个域。因为“日”和“星期”会相互影响,当“日”和“星期”中有一个使用了*以外的规则时,另一个应该使用?而不能使用*。

    想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法:
    日:20,月:*,星期:?
    
  • L 表示最后,只能出现在“日”和“星期”域。

    在“星期”域使用5L,意味着在最后的一个星期四触发
    
  • W 表示有效工作日(周一到周五),只能出现在“日”域,系统将在离指定日期的最近的有效工作日触发事件。

    在day使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;
    如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
    
    
  • LW 这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五

  • # 用于确定每个月第几个星期几,只能出现在“星期”域。

    在4#2,表示某月的第二个星期三
    

把上面的代码稍做修改就可以定时切换壁纸了:

# -*- coding: utf-8 -*-

import os, random
import win32api, win32gui, win32con
from apscheduler.schedulers.blocking import BlockingScheduler

def set_wallpaper(photo_path):
    """设置壁纸"""

    # 1.打开注册表键
    key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)

    # 2.设置壁纸风格:0=居中 1=平铺 2=拉伸
    win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, "2") 

    # 3.设置壁纸是否缩放:0=缩放 1=原图
    win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, "0")

    # 4.设置壁纸
    win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, photo_path, 3)

    # 5.关闭注册表键
    win32api.RegCloseKey(key)
    
def set_wallpaper_random(photo_dir):
    """随机设置壁纸"""

    wall_papers = list()
    for root, dirs, files in os.walk(photo_dir):
        for name in files:
            if os.path.splitext(name)[1].lower() == ".jpg":
                wall_papers.append(os.path.join(root, name))
    
    set_wallpaper(random.choice(wall_papers))
    
def set_wallpaper_regular(photo_dir):
    """定时设置壁纸"""

    scheduler = BlockingScheduler()
    scheduler.add_job(set_wallpaper_random,
        args    = (photo_dir, ),
        trigger = "cron",
        second  = "*/10" # 每10分钟切换一次
    )          
    scheduler.start()
    
if __name__ == '__main__':
    #photo_path = r'D:\CSDN\Column\desktop\album\20200129150646.jpg'
    #set_wallpaper(photo_path)
    #set_wallpaper_random(r'D:\CSDN\Column\desktop\album')
    set_wallpaper_regular(r'D:\CSDN\Column\desktop\album')

运行这段段代码,每隔10分钟自动切换壁纸。有兴趣的同学,还可以打包成服务程序,甚至做成任务托盘程序,将会更加实用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章