【Python】Tkinterでタスクトレイに常駐させる【SystemTray App】

  • 投稿 : 2021-09-20
  • 更新 : 2022-01-24
Windows10
Python 3.8.5
で動作確認しています

追記:2022/01/24
Python 3.10.2でも動作しました

実現したいこと

・TkinterのGUI
・常駐する
・タスクトレイにアイコン
・Windowを閉じても、定期的に実行される

デジタル時計の作成

import tkinter as tk
import datetime
import time

#===========================
# タイマーイベント関数
#===========================
def time_update():
    now = datetime.datetime.now()
    tm = "{:02}:{:02}:{:02}".format(now.hour, now.minute, now.second)

    canvas.delete("all")
    canvas.create_text(100, 50, text=tm, font=("",36))

    root.after(1000, time_update)

#===========================
# メイン 関数
#===========================
def main():

   global root
   global canvas

   #-----------------------
   # フォーム表示
   #-----------------------
   root = tk.Tk()
   root.title("時計")
   root.geometry("200x100")
   root.resizable(False, False)

   canvas = tk.Canvas(master=root, width=200, height=100)
   canvas.place(x=0, y=0)

   #-----------------------
   # タイマー
   #-----------------------
   root.after(1000, time_update)

   #-----------------------
   # Xボタンを押された時の処理
   #-----------------------
   #root.protocol('WM_DELETE_WINDOW', lambda:root.withdraw())

   #-----------------------
   # イベント待機
   #-----------------------
   root.mainloop()

#===========================
# 実行
#===========================
main()

root.after(1000, time_update)を使うのがコツ

Xボタンを押された時の処理は、コメントアウト(コメント化)されてますが、常駐化アプリにするときに使います。

タスクトレイにアイコン表示

ライブラリの導入

pip install pystray

プログラム作成

https://githubmemory.com/repo/firedm/FireDM/issues/241

上記を参考に、pystrayの部分の型枠だけを作成しました

import threading
import pystray
from pystray import Icon, Menu, MenuItem
from PIL import Image

#===========================
# スレッド関係の関数
#===========================
def thread_st():
   global icon

   #-----------------------
   # メニュー
   #-----------------------
   options_map = {'Quit': lambda: thread_quit()}
    
   items = []
   for option, callback in options_map.items():
        items.append(MenuItem(option, callback, default=True if option == 'Show' else False))

   menu = Menu(*items)
   
   #-----------------------
   # アイコン表示
   #-----------------------
   image = Image.open("favicon.ico")
   icon=pystray.Icon("name", image, "My System Tray Icon", menu)
   icon.run()

def thread_quit():
   global icon
   icon.stop()

#===========================
# メイン 関数
#===========================
def main():
   
   #-----------------------
   # スレッド開始
   #-----------------------
   threading.Thread(target=thread_st).start()

#===========================
# 実行
#===========================
main()

アイコンを表示させるだけなら、スレッドを使う必要はありません。あと、上記のサンプルは、Windows環境以外では動作しないと思います。

デジタル時計の常駐アプリ化

今まで作成した2つのプログラムをマージ(合成)する形で、時計アプリを常駐アプリ化します

import tkinter as tk
import datetime
import time

import threading
import pystray
from pystray import Icon, Menu, MenuItem
from PIL import Image

#===========================
# タイマーイベント関数
#===========================
def time_update():

    now = datetime.datetime.now()
    tm = "{:02}:{:02}:{:02}".format(now.hour, now.minute, now.second)

    canvas.delete("all")
    canvas.create_text(100, 50, text=tm, font=("",36))

    root.after(1000, time_update)

#===========================
# スレッド関係の関数
#===========================
def thread_st():
   global icon
   global root

   #-----------------------
   # メニュー
   #-----------------------
   options_map = {'Show': lambda:[print('show_main_window'),root.after(0,root.deiconify)], 'Quit': lambda: root.after(1, thread_quit)} #変更 update
    
   items = []
   for option, callback in options_map.items():
        items.append(MenuItem(option, callback, default=True if option == 'Show' else False))

   menu = Menu(*items)
   
   #-----------------------
   # アイコン表示
   #-----------------------
   image = Image.open("favicon.ico")
   icon=pystray.Icon("name", image, "My System Tray Icon", menu)
   icon.run()

def thread_quit():
   global icon
   global root

   icon.stop()
   root.destroy()   #追加 add

#===========================
# メイン 関数
#===========================
def main():

   global root
   global canvas

   #-----------------------
   # フォーム表示
   #-----------------------
   root = tk.Tk()
   root.title("時計")
   root.geometry("200x100")
   root.resizable(False, False)

   canvas = tk.Canvas(master=root, width=200, height=100)
   canvas.place(x=0, y=0)

   #-----------------------
   # タイマー
   #-----------------------
   root.after(1000, time_update)

   #-----------------------
   # Xボタンを押された時の処理
   #-----------------------
   root.protocol('WM_DELETE_WINDOW', lambda:root.withdraw())

   #-----------------------
   # スレッド開始
   #-----------------------
   threading.Thread(target=thread_st).start()

   #-----------------------
   # イベント待機
   #-----------------------
   root.mainloop()

#===========================
# 実行
#===========================
main()

・右クリックメニューのメニュー部分
・Xボタン押下時の処理
・thread_quit()のroot.destroy()

マージして、上記のようなところを少し追加・修正しています。

動画での説明

Windowを閉じても、定期的(1秒毎)に実行されるのを、動画の最後のほうで確認しています。

#--------------
# 動作確認用
#--------------
f = open('./file00.txt', 'a')
f.write(tm)
f.write("\n")
f.close()
#--------------

上記のようなコードを挿入して、ファイルに時刻が出力されることで確認しました。

スポンサーリンク