Pythonで壁紙を変更するGUIをtkinterで作ってみた
作りたいもの
tkinterで設定した壁紙一覧から好きな壁紙を選んで簡単に壁紙を変更するguiを作る
背景
pcの壁紙をかっこいい壁紙や可愛い壁紙あとちょっとえっちな壁紙とかを設定したいけど他の人には見られたくない...
でも、いちいち変更するのってめんどいよなって思ったので調べてみたらできそうだったので作ってみることにしました!
環境
- windows10
- python 3.7.9
できたもの
アプリ使用例
壁紙には例としてswitchに保存されてた画像を使いました
使用したライブラリ
- ctypes
- json
- os
- tkinter
- filedialog
- messagebox
どのライブラリも標準ライブラリなので新たにpip install
する必要はありません。
ctypes
ctypes は Python のための外部関数ライブラリです。このライブラリは C と互換性のあるデータ型を提供し、動的リンク/共有ライブラリ内の関数呼び出しを可能にします。動的リンク/共有ライブラリを純粋な Python でラップするために使うことができます。(公式引用)
難しいことが書いてありますが、この記事で最も重要となるpcの壁紙を変更するためのライブラリになります。次のように記述することで壁紙を変更することができます。
ctypes.windll.user32.SystemParametersInfoW(20, 0, image_path, 3)
注意点ですが、この方法はWindowsでのみ使用できます。ちなみにWindowsの32bit版の場合は、先ほどのコードのInfoWのWをAに変更したら行けるらしいです(未検証)。
この壁紙を変更するのは、ctypesのほんの一部の使い方なので時間がある方は他にどんなことができるのか調べてみても面白いと思います!!
json
json形式で壁紙の名前と画像の絶対パスを読み書きするために使います。
初回はpython file
と同じ階層にjsonファイルが生成され、次回以降は同じ階層にあるjsonファイルを参照します。
os
ファイル、フォルダの作成や削除などファイル関係で色々できるライブラリです。
今回はjsonファイルを参照して得た絶対パスに画像が存在しているかを確認するために使用します。
tkintr
python標準ライブラリのguiツールキットになります。
細かい使い方などは他の方の記事とかを確認してください
filedialog
from tkniter import filedialog
と書くと使うことができます。
filedialog.askopenfilename()
というコードでpc内の任意のファイルを選択し、絶対パスを戻り値に受け取ることができます。
messagebox
from tkniter import messagebox
と書くと使うことができます。
色々なメッセージボックスを表示させることができます。
今回は利用者にエラーを知らせるために使用しています。
コードの解説
全体コード
まずは全体のコードです
次に細かく説明を入れます
import ctypes
import json
import os
from tkinter import *
from tkinter import filedialog, messagebox
class WallpaperChange(Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.file = {}
try:
with open("wallpaper_list.json", "r", encoding="utf-8") as f:
self.file = json.load(f)
except:
pass
self.master.geometry("200x280")
self.master.title("wallpaper change")
self.master.resizable(False, False)
self.create_widget()
def create_widget(self):
self.listbox = Listbox(self.master, height=15, width=31, selectmode="single", takefocus=0)
self.listbox.place(x=5, y=5)
self.listbox.bind("<Return>", self.change)
self.listbox.bind("<BackSpace>", self.delete)
button = Button(self.master, text="壁紙追加", command=self.select_file)
button.place(x=140, y=252)
for name in self.file.keys():
self.listbox.insert(END, name)
def select_file(self):
def this_destroy():
if entry.get() not in self.file.keys():
self.file[entry.get()] = filename
try:
with open("wallpaper_list.json", "r", encoding="utf-8") as f:
json_list = json.load(f)
except:
json_list = {}
json_list[entry.get()] = filename
with open("wallpaper_list.json", "w", encoding="utf-8") as f:
json.dump(json_list, f, indent=2, ensure_ascii=False)
self.listbox.insert(END, entry.get())
select_frame.destroy()
else:
messagebox.showerror("エラー", "この文字はすでに使用されています")
filename = filedialog.askopenfilename(filetypes=[("Available file", ".png .jpg .jpeg .bmp .dib .tiff .wdp"), ("PNG", ".png"), ("JPEG", ".jpg .jpeg"), ("BITMAP", ".bmp .dib"), ("TIFF", ".tiff"), ("WDP", ".wdp")])
if filename == "":
return
select_frame = Toplevel(self)
select_frame.title("選択")
select_frame.geometry("200x100")
select_frame.grab_set()
select_frame.focus_set()
select_frame.transient(self.master)
label = Label(select_frame, text="表示時のニックネームを決めてください")
label.place(x=10, y=10)
entry = Entry(select_frame)
entry.insert(END, filename.split("/")[-1])
entry.place(x=12, y=32)
button = Button(select_frame, text="決定", command=this_destroy)
button.place(x=160, y=30)
def delete(self, event):
self.file.pop(event.widget.get(event.widget.curselection()))
event.widget.delete(event.widget.curselection())
with open("wallpaper_list.json", "w", encoding="utf-8") as f:
json.dump(self.file, f, indent=2, ensure_ascii=False)
def change(self, event):
image_path = self.file[event.widget.get(event.widget.curselection())]
if os.path.isfile(image_path):
ctypes.windll.user32.SystemParametersInfoW(20, 0, image_path, 3)
self.master.destroy()
else:
messagebox.showerror("エラー", "対象ファイルが存在しないか参照出来ません。\n一度削除してもう一度壁紙を設定し直してください")
def main():
root = Tk()
app = WallpaperChange(master=root)
app.mainloop()
if __name__ == "__main__":
main()
init
self.file = {}
try:
with open("wallpaper_list.json", "r", encoding="utf-8") as f:
self.file = json.load(f)
except:
pass
ここでは、このPythonファイルのカレントディレクトリにwallpaper_list.jsonというjsonファイルがある場合には格納されている自分でつけたニックネームと、その画像ファイルがある絶対パスをself.fileに辞書型で保存し、ない場合にはpassして何もしないようにしている
self.master.geometry("200x280")
self.master.title("wallpaper change")
self.master.resizable(False, False)
self.create_widget()
この4行では、geometryで表示画面の大きさを設定し、titleでタイトルを設定し、resizableで表示画面の大きさを自分自身で変更できないようにしている
最後にself.create_widget()でボタンなどのwidgtを設置する
create_widget
メインの画面に表示させるリストボックスとボタンの設置を行う
リストボックスはbindを用いてEnterを押したらchange関数で壁紙を変更し、Back Spaceを押したらdelete関数でリストボックスから削除することができるようにした
for name in self.file.keys():
self.listbox.insert(END, name)
上記のコードでリストボックス設置後にself.fileの中身をリストボックスに反映させる処理をしている
select_file
表示画面の「壁紙追加」ボタンを押すことで呼び出される
filedialog.askopenfilename()
上記のコードによりファイルダイアログを表示し、追加したい壁紙を選択できるようにしている
その後Toplevel関数にて新たな画面を作製し、そこでニックネームをつけれるようにしている
delete
リストボックスから対象の画像を削除し、jsonファイルを書き換える
change
ctypes関数を使い壁紙を変更する
image_path = self.file[event.widget.get(event.widget.curselection())]
画像のフルパスは、辞書型のself.fileに設定した画像のニックネーム(辞書のkey)を入力し得ている
最後に
今回は、windowsの壁紙を自由に設定したものに変更できるguiを作りました
調べたところwindows以外でも違う関数を使うことで実現できそうなのでやりたい人は調べてやってみて下さい
せっかく最初考えていた通りのguiを作れたので次回はこのtkinterで作成したものをexe化して配布できるようにしていきたいと思います(ほんとはもうexe化してるけど書くのが苦手すぎて進まない...)
始めての記事で右も左もわからない状態ですが、これから何か作ったり、できるようになったら更新していこうと思いますのでもし良かったら記事の見やすさを向上させるためにも色々アドバイスくれたらうれしいです
ではまた
Discussion