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分鐘自動切換壁紙。有興趣的同學,還可以打包成服務程序,甚至做成任務托盤程序,將會更加實用。

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