moses-palmer / pystray

GNU General Public License v3.0
473 stars 59 forks source link

Error when left click icon when creating menu by using a tuple #86

Closed coding-pivo closed 3 years ago

coding-pivo commented 3 years ago

Hi, when I create the menu by using a tuple as argument as shown below everything is working as expected beside the left click on the tray icon will lead to the error mentioned below. I'm using Win10 and Python 3.9.1 (64bit). Maybe I'm doing something wrong here but according to documentation a tuple would be a valid parameter for the menu class.

Code:

    image = Image.open("icon.ico")
    icon = pystray.Icon(name="tool", icon=image, title="Tool - 0.0.1")
    icon.menu = (pystray.MenuItem('Test', lambda : print('Test1'), default=True), pystray.MenuItem('Submenu', pystray.Menu((pystray.MenuItem('Test2', lambda : print('Test2'))),)), pystray.MenuItem('Exit', lambda : tray_icon_exit(icon)))
    icon.run()

Error:

Traceback (most recent call last):
  File "C:\Python39\lib\site-packages\pystray\_win32.py", line 398, in _dispatcher
    return int(icon._message_handlers.get(
  File "C:\Python39\lib\site-packages\pystray\_win32.py", line 187, in _on_notify
    self()
  File "C:\Python39\lib\site-packages\pystray\_base.py", line 88, in __call__
    self._menu(self)
TypeError: 'tuple' object is not callable
ArvidAnderson commented 3 years ago

I have the same problem!

coding-pivo commented 3 years ago

I got my code running now with a dynamic menu creation. Maybe an example could be added to the documentation as I had a hard time to collect all the information needed. I will post the example later on here. Feel free to use it.

moses-palmer commented 3 years ago

Thank you for your offer.

Did you find the documentation here lacking, or non-obvious to locate?

coding-pivo commented 3 years ago

Welcome. It's not a problem of locating the documentation and also not lacking. Probably because of my limited python experience it would be helpful to have more examples ;) For a beginner like me it was a little bit hard to understand how to set up all needed stuff properly. But I'm really satisfied with the result of my code now and do much appreciate your work here!

coding-pivo commented 3 years ago

Hi, just for reference here is an example code that will generate a tray icon menu from the folder/file structure. Probably not perfect Python code and might be optimized but working ;)

#!/usr/bin/env python

from __future__ import print_function
import os
import subprocess
import pystray
from PIL import Image

def tray_icon_get_menu(menu_list):
    return menu_list

def tray_icon_setup(icon):
    icon.visible = True

def tray_icon_exit(icon):
    icon.visible = False
    icon.stop()

def tray_icon_add_menu_item(file_name):
    return [pystray.MenuItem(os.path.splitext(os.path.basename(file_name))[0], lambda : print(file_name))]

def tray_icon_add_sub_menu_item(name, sub_list):
    return [pystray.MenuItem(name, pystray.Menu(*tray_icon_get_menu(sub_list)))]

def get_sub_menus(directory):
    # get all directories in this folder in alphabetical order
    subdirs = [f for f in os.listdir(directory) if os.path.isdir(os.path.join(directory, f))]
    subdirs.sort()

    # get all txt files in this directory in alphabetical order
    subfiles = [f for f in os.listdir(directory) if (os.path.isfile(os.path.join(directory, f)) and os.path.splitext(os.path.join(directory, f))[1] == '.txt')]
    subfiles.sort()

    sub_menu_list = []

    # For each sub generate a sub menu item and call this function again (recursion)
    for f in subdirs:
        sub_menu_list += tray_icon_add_sub_menu_item(f, get_sub_menus(directory + os.sep + f))

    # For each file create a menu item
    for f in subfiles:
        sub_menu_list += tray_icon_add_menu_item(directory + os.sep + f)

    return sub_menu_list

def tray_icon_add_menu():
    # Check Example folder is available and create one otherwise
    if not os.path.isdir('Examples'):
        print('No ''Examples'' folder available. Creating a new examples folder...')
        os.mkdir('Examples')
        os.mkdir('.\\Examples\\Example1')
        os.mkdir('.\\Examples\\Example1\\Example11')
        os.mkdir('.\\Examples\\Example2')
        with open('.\\Examples\\Example1\\Example11\\example11.txt', 'w') as file:
            pass
        with open('.\\Examples\\Example2\\example2.txt', 'w') as file:
            pass
        with open('.\\Examples\\example.txt', 'w') as file:
            pass
    return get_sub_menus('.\\Examples')

def main():
    image = Image.open("icon.ico")
    icon = pystray.Icon(name="tray_test", icon=image, title="Tray Test")

    main_menu_list = []
    main_menu_list += tray_icon_add_menu()

    main_menu_list.append(pystray.Menu.SEPARATOR)
    main_menu_list.append(pystray.MenuItem('Exit', lambda : tray_icon_exit(icon)))

    icon.menu = pystray.Menu(*tray_icon_get_menu(main_menu_list))
    icon.run(tray_icon_setup(icon))

if __name__ == '__main__':
    main()