python 系统托盘_使用 PySide 实现 Python 系统托盘图示

系统托盘图示 (system tray icon) 通常用来在桌面应用程序最小化后,不希望在任务列(task

bar)出现时,提供一个简单跟使用者交互的接口。 透过这样的用户接口,应用程序可以在有重要事件发生时,即时通知用户。因此,系统托盘图示常被诸如“邮件检查”、“股票报价”等不需要复杂接口的桌面应用所使用。本文山姆锅说明 Python 如何使用 PySide 来实现一个跨平台 (cross-platform) 的系统托盘图示应用程序。

目前三种主要的桌面操作系统,也就是 Windows, Mac OSX, 以及 Linux,都有支持托盘图示接口,但是名称跟支持程度稍有不同。 PySide (QT) 提供一个跨平台的方案,对大部分的 Python

桌面应用来说,这是适合的方案。如果真的不需要其他接口组件, 可以针对各个平台分别来实现系统托盘程序,对于这样的情况,山姆锅建议使用下列组合:

Windows: PyWin32

Ubuntu(Linux): PyGObject

注意:其中 PyGObject 只适合 GTK3 桌面环境。

执行环境

山姆锅假设以下的执行环境:

Python 2.7.x

PySide 1.2.2

QT 4

范例程序

本文使用 PySide 完成一个单纯的桌面程序 AvaShell,可以做到下列功能:

在系统托盘显示一个图示。

用户点选图示后,会弹跳 (pop-up) 一个菜单。

用户可以从菜单选择离开程序。

是的,目前就只能完成上述功能。

为了封装代码, 先定义一个 Shell 抽象类作为后续实现的基础,这样以后可以依照不同环境选用不同实现:

shell_base.pyview raw1

2

3

4

5

6

7

8

9

10

11

12

13from __future__ import absolute_import, division, print_function, unicode_literals

from abc import abstractmethod

class (object):

def (self):

"""Starts up the shell.

"""

pass

抽象方法(method) run 必须由继承的子类来实现。底下是采用 PySide 的实现类:

shell_pyside.pyview raw1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81from __future__ import absolute_import, print_function, unicode_literals

import sys

import logging

from PySide.QtGui import *

from avashell.shell_base import ShellBase

from avashell.utils import resource_path

_logger = logging.getLogger(__name__)

class MainWnd(QMainWindow):

def __init__(self, shell, icon):

super(MainWnd, self).__init__()

self._shell = shell

self.icon = icon

self.context_menu = None

self.tray_icon = None

if not QSystemTrayIcon.isSystemTrayAvailable():

msg = "I couldn't detect any system tray on this system."

_logger.error(msg)

QMessageBox.critical(None, "AvaShell", msg)

sys.exit(1)

self.init_ui()

def init_ui(self):

self.setWindowIcon(self.icon)

self.setWindowTitle('AvaShell')

self.create_tray_icon(self.icon)

self.tray_icon.show()

def on_tray_activated(self, reason=None):

_logger.debug("Tray icon activated.")

def on_quit(self):

self._shell.quit_app()

def create_context_menu(self):

self.quit_action = QAction("&Quit AvaShell", self, triggered=self.on_quit)

menu = QMenu(self)

menu.addAction(self.quit_action)

return menu

def create_tray_icon(self, icon):

self.context_menu = self.create_context_menu()

self.tray_icon = QSystemTrayIcon(self)

self.tray_icon.setContextMenu(self.context_menu)

self.tray_icon.setIcon(icon)

self.tray_icon.activated.connect(self.on_tray_activated)

class Shell(ShellBase):

""" Shell implementation using PySide

"""

def __init__(self):

super(Shell, self).__init__()

self.app = QApplication(sys.argv)

self.app.setQuitOnLastWindowClosed(False)

self.icon = QIcon(resource_path('res/icon.png'))

self.menu = None

self.wnd = MainWnd(self, self.icon)

def quit_app(self):

self.app.quit()

def run(self):

_logger.info("Shell is running...")

self.app.exec_()

if __name__ == '__main__':

shell = Shell()

shell.run()

Shell 为 ShellBase 的子类,负责建构 QApplication 以及主窗口(MainWnd); 主窗口负责建立 QSystemTrayIcon 这个代表系统托盘的对象,以及它使用的弹出式菜单,同时注册相关事件处理器。由于主窗口并不显示,且不希望主窗口被关闭的时候, 程序自动离开,标号 1 的叙述就是通知 PySide 不要自动离开程序。

为了找到图档资源(resource),另外定义了一个公用函数 resource_path,以便之后在使用 PyInstaller 打包后能正常运行。此公用函数定义在 utils.py 这个文件中。

utils.pyview raw1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18from __future__ import absolute_import, division, print_function, unicode_literals

import os

import sys

def resource_path(relative):

"""Gets the resource's absolute path.

:param relative: the relative path to the resource file.

:return: the absolute path to the resource file.

"""

if hasattr(sys, "_MEIPASS"): # 1

return os.path.join(sys._MEIPASS, relative)

abspath = os.path.abspath(os.path.join(__file__, "..")) # 2

abspath = os.path.dirname(abspath)

return os.path.join(abspath, relative)

标号 1 的叙述判断是否在经过 PyInstaller 打包好的环境执行,如果是则直接使用 sys._MEIPASS 这个特殊属性值作为资源路径; 如果不是, resource_path 则假设资源文件放在它的上层目录。 因此,标号 2 的叙述需要根据 utils.py 与资源的相对位置作调整。

参考数据

结语

使用 PySide 或者 PyQT 可以很方便地实现跨平台的图形接口,对于大部分的桌面应用来说,这是好事。但是对于只需要一个简单的系统托盘图示的应用来说,使用 PySide 意味着额外需要十几 MB 的空间来散布所开发的应用程序。 即使如此,相对于针对各个平台开发所需的时间来说,使用 PySide 通常是比较合理的作法。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值