【问题标题】:Quick and easy: trayicon with python?快速简单:trayicon 与 python?
【发布时间】:2011-09-17 09:21:06
【问题描述】:

我只需要一个简单的示例,了解如何轻松地将带有 python 的图标放在我的系统托盘上。这意味着:我运行程序,没有显示任何窗口,只有一个托盘图标(我有一个 png 文件)出现在系统托盘中,当我右键单击它时,会出现一个带有一些选项的菜单(当我单击在一个选项上,一个函数运行)。 那可能吗?我根本不需要任何窗户...

示例/代码 sn-ps 非常感谢! :D

【问题讨论】:

  • 什么操作系统?如果是 linux,我会查看 gmail-notify.py 的源代码。见gmail-notify.sourceforge.net/download.php
  • 我想这并不重要,因为雾鸟的 wx 代码在 linux 中运行良好(特别是 gnome 系统面板)。

标签: python wxpython pyqt systray tray


【解决方案1】:

适用于 Windows 和 Gnome

给你! wxPython 是炸弹。改编自我的Feed Notifier 应用程序的源代码。

import wx

TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'


def create_menu_item(menu, label, func):
    item = wx.MenuItem(menu, -1, label)
    menu.Bind(wx.EVT_MENU, func, id=item.GetId())
    menu.AppendItem(item)
    return item


class TaskBarIcon(wx.TaskBarIcon):
    def __init__(self):
        super(TaskBarIcon, self).__init__()
        self.set_icon(TRAY_ICON)
        self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Say Hello', self.on_hello)
        menu.AppendSeparator()
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

    def set_icon(self, path):
        icon = wx.IconFromBitmap(wx.Bitmap(path))
        self.SetIcon(icon, TRAY_TOOLTIP)

    def on_left_down(self, event):
        print 'Tray icon was left-clicked.'

    def on_hello(self, event):
        print 'Hello, world!'

    def on_exit(self, event):
        wx.CallAfter(self.Destroy)


def main():
    app = wx.PySimpleApp()
    TaskBarIcon()
    app.MainLoop()


if __name__ == '__main__':
    main()

【讨论】:

  • 没问题。如果您也想在菜单项上显示图标,请使用 create_menu_item 中的 item.SetBitmap(wx.Bitmap(path))。 (只是为了让它看起来不错。)
  • 我也会考虑使用 PyEmbeddedImage:wxpython.org/docs/api/…
  • 图标没有出现在 Ubuntu 12.10 Unity 托盘上。没有错误,只是看不到它。
  • Unity 使用 appindicator 而不是 wxWidgets 需要的 freedesktop.org 系统托盘协议。 docs.wxwidgets.org/trunk/classwx_task_bar_icon.html
  • 我得到了 AttributeError:'module' 对象没有属性 'TaskBarIcon'。我的python版本是2.7
【解决方案2】:

wx.PySimpleApp 已弃用,这里是如何使用 wx.App 代替

我花了一段时间才弄清楚这一点,所以我想我会分享。 wx.PySimpleApp 在 wxPython 2.9 及更高版本中已弃用。这是 FogleBird 使用 wx.App 代替的原始脚本。

import wx

TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'

def create_menu_item(menu, label, func):
    item = wx.MenuItem(menu, -1, label)
    menu.Bind(wx.EVT_MENU, func, id=item.GetId())
    menu.AppendItem(item)
    return item

class TaskBarIcon(wx.TaskBarIcon):
    def __init__(self, frame):
        self.frame = frame
        super(TaskBarIcon, self).__init__()
        self.set_icon(TRAY_ICON)
        self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Say Hello', self.on_hello)
        menu.AppendSeparator()
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

    def set_icon(self, path):
        icon = wx.IconFromBitmap(wx.Bitmap(path))
        self.SetIcon(icon, TRAY_TOOLTIP)

    def on_left_down(self, event):
        print 'Tray icon was left-clicked.'

    def on_hello(self, event):
        print 'Hello, world!'

    def on_exit(self, event):
        wx.CallAfter(self.Destroy)
        self.frame.Close()

class App(wx.App):
    def OnInit(self):
        frame=wx.Frame(None)
        self.SetTopWindow(frame)
        TaskBarIcon(frame)
        return True

def main():
    app = App(False)
    app.MainLoop()


if __name__ == '__main__':
    main()

【讨论】:

  • 运行此代码返回:AttributeError: 'module' object has no attribute 'TaskBarIcon'
  • @Pitto 与此处相同,但我发现一些类和常量已移至名为 wx.adv 的子包中,因此您应该使用 wx.TaskBarIcon 而不是 wx.TaskBarIconEVT_TASKBAR_LEFT_DOWN 相同
  • 此解决方案适用于 Gnome 3.26 (Ubuntu 18.04) 和 + 如果启用了扩展 TopIcon。系统托盘在现代 Gnome 中消失了,应用程序指示器取代了它们。有人知道如何使用 WX 创建应用程序指标吗?这个link 展示了如何在没有 WX 的情况下做一个。
【解决方案3】:

2018版

import wx.adv
import wx
TRAY_TOOLTIP = 'Name' 
TRAY_ICON = 'icon.png' 


def create_menu_item(menu, label, func):
    item = wx.MenuItem(menu, -1, label)
    menu.Bind(wx.EVT_MENU, func, id=item.GetId())
    menu.Append(item)
    return item


class TaskBarIcon(wx.adv.TaskBarIcon):
    def __init__(self, frame):
        self.frame = frame
        super(TaskBarIcon, self).__init__()
        self.set_icon(TRAY_ICON)
        self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Site', self.on_hello)
        menu.AppendSeparator()
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

    def set_icon(self, path):
        icon = wx.Icon(path)
        self.SetIcon(icon, TRAY_TOOLTIP)

    def on_left_down(self, event):      
        print ('Tray icon was left-clicked.')

    def on_hello(self, event):
        print ('Hello, world!')

    def on_exit(self, event):
        wx.CallAfter(self.Destroy)
        self.frame.Close()

class App(wx.App):
    def OnInit(self):
        frame=wx.Frame(None)
        self.SetTopWindow(frame)
        TaskBarIcon(frame)
        return True

def main():
    app = App(False)
    app.MainLoop()


if __name__ == '__main__':
    main()

【讨论】:

  • 为什么需要框架?为什么使用 wxPython 基础教程中的 self.Destroy() 而不是 self.Close()?
【解决方案4】:

如果你能保证windows,又不想引入wx的重度依赖,可以用pywin32 extensions做到这一点。

另见question

【讨论】:

  • @kbec,感谢您的提醒。我用一些更新的链接更新了我的答案。看起来我重复了一个较旧的答案,所以我被包括在内以删除我的答案,但为了谷歌的缘故,我会把它留在这里。
【解决方案5】:

对于 Ubuntu

class TrayIcon:
    def init():


iconPath = {"Windows":os.path.expandvars("%PROGRAMFILES%/MyProgram/icon.png"),
                  "Linux":"/usr/share/icons/myprogramicon.png"}        
    if platform.system()=="Linux":
        import gtk
        import appindicator # Ubuntu apt-get install python-appindicator 

    # Create an application indicator
    try:
        gtk.gdk.threads_init()
        gtk.threads_enter()
        icon = iconPath[platform.system()]
        indicator = appindicator.Indicator("example-simple-client", "indicator-messages", appindicator.CATEGORY_APPLICATION_STATUS)
        indicator.set_icon(icon)
        indicator.set_status (appindicator.STATUS_ACTIVE)
        indicator.set_attention_icon ("indicator-messages-new")
        menu = gtk.Menu()

        menuTitle = "Quit"   
        menu_items = gtk.MenuItem(menuTitle)
        menu.append(menu_items)
        menu_items.connect("activate", TrayIcon.QuitApp, menuTitle)
        menu_items.show()

        menuTitle = "About My Program"
        menu_items = gtk.MenuItem(menuTitle)
        menu.append(menu_items)
        menu_items.connect("activate", TrayIcon.AboutApp, menuTitle)
        menu_items.show()   

        indicator.set_menu(menu)    
    except:
        pass

    # Run the app indicator on the main thread.
    try:

        t = threading.Thread(target=gtk.main)
        t.daemon = True # this means it'll die when the program dies.
        t.start()
        #gtk.main()

    except:
        pass
    finally:
        gtk.threads_leave()     

@staticmethod
def AboutApp(a1,a2):
    gtk.threads_enter()
    dialog = gtk.Dialog("About",
                        None,
                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                        (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
    label = gtk.Label("My Program v0.0.1, (C)opyright ME 2015. All rights reserved.")
    dialog.vbox.pack_start(label)
    label.show()
    label2 = gtk.Label("example.com\n\nFor more support contact me@gmail.com")
    label2.show()
    dialog.action_area.pack_end(label2)
    response = dialog.run()
    dialog.destroy()
    gtk.threads_leave()

@staticmethod
def QuitApp(a1, a2):
    sys.exit(0)

跨平台

PyQt: Show menu in a system tray application

【讨论】:

    【解决方案6】:

    Three 是一个名为 pystray 的包(不好的名字,大声说出来),但它的作用就像一个魅力,并且比 wx ot qt 更轻量级。这些是链接
    https://pystray.readthedocs.io/en/latest/index.html
    https://pypi.org/project/pystray/

    【讨论】:

      【解决方案7】:

      如果您尝试在后台运行基于 python 的程序,您可以将其作为服务运行。看看这个活动状态配方它非常有用。我相信其中一种选择是使用 py2exe 或 pyinstall 将您的应用程序转换为 exe。

      http://code.activestate.com/recipes/551780/

      【讨论】:

      • 是否可以将 GUI 作为服务运行?
      • @Isquare1 我的 python 知识有点过时,但this SO question 似乎可以回答。
      【解决方案8】:

      是的。 wiki.wxpython.org 上有一个跨平台示例,我在 macOS High Sierra (10.13.3)、Windows 7 和 gnome 3/centos7 上使用 python 2.7 (minconda install) 进行了测试。它在这里(忽略页面标题): https://wiki.wxpython.org/Custom%20Mac%20OsX%20Dock%20Bar%20Icon

      python 3.6 需要小模块:

      • 您必须导入 wx.adv
      • wx.TaskBarIcon 变为 wx.adv.TaskBarIcon
      • wx.IconFromBitmap 变成 wx.Icon

      Gnome 3 需要安装 TopIcons Plus。

      由于您不想显示窗口(“没有窗口显示,只是一个托盘图标”),只需注释掉以下行(尽管您仍然希望保留 wx.Frame 父级):

      frame.Show(True)
      

      由于您想使用自己的 .png 图标,请删除 WXPdemo 图像和嵌入图像的内容并替换

      icon = self.MakeIcon(WXPdemo.GetImage())
      

      例如

      icon = wx.Icon('icon.png')
      

      根据我的经验,这将为适应或进一步扩展提供一个良好的开端。

      【讨论】:

        【解决方案9】:

        例如,参考这个帖子->wx question.

        wxPython "classic" -> [new API] wxPython 'Phoenix' (Py3)

        【讨论】:

          猜你喜欢
          • 2011-03-02
          • 2023-03-29
          • 2013-08-07
          • 2016-05-25
          • 1970-01-01
          • 2010-12-11
          • 2014-11-28
          • 2015-01-09
          相关资源
          最近更新 更多