Windows OSにおいて、アプリケーションのタスクトレイアイコンは非常に重要な役割を果たします。Pythonを使ってアプリケーションのタスクトレイアイコンを表示する方法を紹介します。

pystrayを使った方法

pystrayはPythonでタスクトレイアイコンを簡単に表示できるライブラリです。以下は、pystrayを使ったアプリケーションのタスクトレイアイコンの表示方法です。

  1. pystrayをインストールします。

    pip install pystray
    
  2. 以下のコードを実行して、アプリケーションのタスクトレイアイコンを表示します。

    import pystray
    from PIL import Image
    
    def on_exit():
        print('exit')
    
    image = Image.open('icon.png')
    menu = pystray.Menu(pystray.MenuItem('Exit', on_exit))
    tray_icon = pystray.Icon('name', image, 'title', menu)
    tray_icon.run()
    

    icon.pngはアプリケーションのアイコン画像、nameはアプリケーションの名前、titleはタスクトレイアイコンのツールチップに表示するテキストです。on_exitは、タスクトレイアイコンのコンテキストメニューからExitを選択したときに実行される関数です。

win32apiを使った方法

win32apiを使って、Windows APIを直接呼び出してタスクトレイアイコンを表示することもできます。以下は、win32apiを使ったアプリケーションのタスクトレイアイコンの表示方法です。

  1. win32apiをインストールします。

    pip install pypiwin32
    
  2. 以下のコードを実行して、アプリケーションのタスクトレイアイコンを表示します。

    import os
    import win32api
    import win32gui
    import win32con
    from ctypes import windll
    
    class SysTrayIcon:
    
        def __init__(self, icon, hover_text, menu_options, on_quit=None, default_menu_index=None,
                     window_class_name=None):
    
            self.icon = icon
            self.hover_text = hover_text
            self.on_quit = on_quit
    
            menu_options = menu_options + (('Quit', None, self.quit),)
            self._next_action_id = 1
            self.menu_actions_by_id = set()
            self.menu_options = self._add_ids_to_menu_options(list(menu_options))
            self.menu_actions_by_id = dict(self.menu_actions_by_id)
            del self._next_action_id
    
            self.default_menu_index = (default_menu_index or 0)
            self.window_class_name = window_class_name or "SysTrayIconPy
    "
    
            message_map = {
                win32gui.RegisterWindowMessage("TaskbarCreated"): self.restart,
                win32con.WM_DESTROY: self.destroy,
                win32con.WM_COMMAND: self.command,
                win32con.WM_USER + 20: self.notify,
            }
    
            # Register the Window Class
            self.wc = win32gui.WNDCLASS()
            self.hinst = self.wc.hInstance = win32api.GetModuleHandle(None)
            self.wc.lpszClassName = self.window_class_name
            self.wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
            self.wc.hCursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
            self.wc.hbrBackground = win32con.COLOR_WINDOW
            self.wc.lpfnWndProc = message_map  # could also specify a wndproc.
            self.classAtom = win32gui.RegisterClass(self.wc)
    
        def _add_ids_to_menu_options(self, menu_options):
            result = []
            for menu_option in menu_options:
                option_text, option_icon, option_action = menu_option
                if callable(option_action) or option_action is None:
                    action_id = self._next_action_id
                    self._next_action_id += 1
                    self.menu_actions_by_id.add((action_id, option_action))
                else:
                    action_id = option_action
                result.append((option_text, option_icon, action_id))
            return result
    
        def run(self):
            # Create the Window
            style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
            self.hwnd = win32gui.CreateWindow(self.classAtom,
                                              self.window_class_name,
                                              style,
                                              0,
                                              0,
                                              win32con.CW_USEDEFAULT,
                                              win32con.CW_USEDEFAULT,
                                              0,
                                              0,
                                              self.hinst,
                                              None)
            win32gui.UpdateWindow(self.hwnd)
            self.notify_id = None
            self.refresh_icon()
    
            win32gui.PumpMessages()
    
        def refresh_icon(self):
            # Try and find a custom icon
            hinst = win32api.GetModuleHandle(None)
            if os.path.isfile(self.icon):
                icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
                hicon = win32gui.LoadImage(hinst, self.icon, win32con.IMAGE_ICON, 0, 0, icon_flags)
            else:
                hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
    
            if self.notify_id:
                message = win32gui.NIM_MODIFY
            else:
                message = win32gui.NIM_ADD
            self.notify_id = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, win32con.WM_USER + 20, hicon, self.hover_text)
            win32gui.Shell_NotifyIcon(message, self.notify_id)
    
        def restart(self, hwnd, msg, wparam, lparam):
            self.refresh_icon()
    
        def destroy(self, hwnd, msg, wparam, lparam):
            if self.on_quit:
                self.on_quit(self)
            nid = (self.hwnd, 0)
            win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
            win32gui.PostQuitMessage(0)  # Terminate the app.
    
        def notify(self, hwnd, msg, wparam, lparam):
            if l