본문 바로가기
PyQt5_

System tray & macOS menus

by 자동매매 2023. 3. 13.

34. System tray & macOS menus

System tray applications (or menu bar applications) can be useful for making common functions available in a small number of clicks. For full desktop applications they’re a useful shortcut to control apps without opening up the whole window.

Qt provides a simple interface for building cross-platform system tray (Windows) or menu bar (macOS) apps. Below is a minimal working example for showing an icon in the toolbar/system tray with a menu. The action in the menu isn’t connected and so doesn’t do anything yet.

Listing 242. further/systray.py

import os

import sys

from PyQt6.QtGui import QIcon

from PyQt6.QtWidgets import (

QAction,

QApplication,

QColorDialog,

QMenu,

QSystemTrayIcon,

)

basedir = os.path.dirname(__file__)

app = QApplication(sys.argv)

app.setQuitOnLastWindowClosed(False)

# Create the icon

icon = QIcon(os.path.join(basedir, "icon.png"))

# Create the tray

tray = QSystemTrayIcon()

tray.setIcon(icon)

tray.setVisible(True)

# Create the menu

menu = QMenu()

action = QAction("A menu item")

menu.addAction(action)

# Add a Quit option to the menu.

quit = QAction("Quit")

quit.triggered.connect(app.quit)

menu.addAction(quit)

# Add the menu to the tray

tray.setContextMenu(menu)

app. exec ()

You’ll notice that there isn’t a QMainWindow, simply because we don’t have any window to show. You can create a window as normal without affecting the behavior of the system tray icon.

ë

The default behavior in Qt is to close an application once all the

active windows have closed. This won’t affect this toy example,

but will be an issue in application where you do create windows

and then close them. Setting

app.setQuitOnLastWindowClosed(False) stops this and will ensure

your application keeps running.

The provided icon shows up in the toolbar (you can see it on the left hand side of the icons grouped on the right of the system tray or menubar).

Figure 236. The icon showing on the menubar.

Clicking (or right-clicking on Windows) on the icon shows the added menu.

Figure 237. The menubar app menu.

This application doesn’t do anything yet, so in the next part we’ll expand this example to create a mini color-picker.

Below is a more complete working example using the built in QColorDialog from Qt to give a toolbar accessible color picker. The menu lets you choose to get the picked color as HTML-format #RRGGBB, rgb(R,G,B) or hsv(H,S,V).

Listing 243. further/systray_color.py

import os

import sys

from PyQt6.QtGui import QIcon

from PyQt6.QtWidgets import (

QAction,

QApplication,

QColorDialog,

QMenu,

QSystemTrayIcon,

)

basedir = os.path.dirname(__file__)

app = QApplication(sys.argv)

app.setQuitOnLastWindowClosed(False)

# Create the icon

icon = QIcon(os.path.join(basedir, "color.png"))

clipboard = QApplication.clipboard()

dialog = QColorDialog()

def copy_color_hex ():

if dialog. exec ():

color = dialog.currentColor()

clipboard.setText(color.name())

def copy_color_rgb ():

if dialog. exec ():

color = dialog.currentColor()

clipboard.setText(

"rgb(%d, %d, %d)"

% (color.red(), color.green(), color.blue())

)

def copy_color_hsv ():

if dialog. exec ():

color = dialog.currentColor()

clipboard.setText(

"hsv(%d, %d, %d)"

% (color.hue(), color.saturation(), color.value())

)

# Create the tray

tray = QSystemTrayIcon()

tray.setIcon(icon)

tray.setVisible(True)

# Create the menu

menu = QMenu()

action1 = QAction("Hex")

action1.triggered.connect(copy_color_hex)

menu.addAction(action1)

action2 = QAction("RGB")

action2.triggered.connect(copy_color_rgb)

menu.addAction(action2)

action3 = QAction("HSV")

action3.triggered.connect(copy_color_hsv)

menu.addAction(action3)

quit = QAction("Quit")

quit.triggered.connect(app.quit)

menu.addAction(quit)

# Add the menu to the tray

tray.setContextMenu(menu)

app. exec ()

As in the previous example there is no QMainWindow for this example. The menu is

created as before, but adding 3 actions for the different output formats. Each action is connected to a specific handler function for the format it represents. Each handler shows a dialog and, if a color is selected, copies that color to the clipboard in the given format.

As before, the icon appears in the toolbar.

Figure 238. The Color picker on the toolbar.

Clicking the icon shows a menu, from which you can select the format of image you want to return.

Figure 239. The Color picker menu

Once you’ve chosen the format, you’ll see the standard Qt color picker window.

Figure 240. The system Color picker window

Select the color you want and click OK. The chosen color will be copied to the clipboard in the requested format. The formats available will product the following output:

Value Ranges

#a2b3cc 00-FF

rgb(25, 28, 29) 0-255

hsv(14, 93, 199) 0-255

Adding a system tray icon for a full app

So far we’ve shown how to create a standalone system tray application with no main window. However, sometimes you may wish to have a system tray icon as well as a window. When this is done, typically the main window can be opened

and closed (hidden) from the tray icon, without closing the application. In this section we’ll look at how to build this kind of application with Qt5.

In principle it’s quite straightforward — create our main window, and connect a signal from an action to the .show() method of the window.

Below is a small tray notes application called "PenguinNotes". When run, it puts a small penguin icon in your system track or macOS toolbar.

Clicking the small penguin icon in the tray will show the window. The window contains a QTextEdit editor into which you can write notes. You can close the window as normal, or by clicking again on the tray icon. The and app will remain

running in the tray. To close the app you can use File › Close — closing will automatically save the notes.

Listing 244. further/systray_window.py

import os

import sys

from PyQt6.QtGui import QIcon

from PyQt6.QtWidgets import (

QAction,

QApplication,

QMainWindow,

QMenu,

QSystemTrayIcon,

QTextEdit,

)

basedir = os.path.dirname(__file__)

app = QApplication(sys.argv)

app.setQuitOnLastWindowClosed(False)

# Create the icon

icon = QIcon(os.path.join(basedir, "animal-penguin.png"))

Create the tray

tray = QSystemTrayIcon() tray.setIcon(icon) tray.setVisible(True)

class MainWindow (QMainWindow): def init (self): super().init()

self.editor = QTextEdit() self.load() # Load up the text from file.

menu = self.menuBar() file_menu = menu.addMenu("&File")

self.reset = QAction("&Reset") self.reset.triggered.connect(self.editor.clear) file_menu.addAction(self.reset)

self.quit = QAction("&Quit") self.quit.triggered.connect(app.quit) file_menu.addAction(self.quit)

self.setCentralWidget(self.editor)

self.setWindowTitle("PenguinNotes")

def load (self): with open("notes.txt", "r") as f: text = f.read() self.editor.setPlainText(text)

def save (self): text = self.editor.toPlainText() with open("notes.txt", "w") as f: f.write(text)

def activate (self, reason): if ( reason == QSystemTrayIcon.ActivationReason.Trigger ): # Icon clicked. self.show()

w = MainWindow()

tray.activated.connect(w.activate)

app.aboutToQuit.connect(w.save)

app. exec ()

Z

On macOS the Quit action will appear in the application menu

(on the far left, with the application name), not the File menu. If

we didn’t also add the File › Reset action, the File menu would

be empty and hidden (try it!)

Below is a screenshot of the notes app with the window open.

Figure 241. The notes editor window.

The control of showing and hiding the window is handled in the activate method on our QMainWindow. This is connected to the tray icon .activated signal at the bottom of the code, using tray.activated.connect(w.activate).

def activate (self, reason):

if reason == QSystemTrayIcon.Trigger: # Icon clicked.

if self.isVisible():

self.hide()

else :

self.show()

This signal is triggered under a number of different circumstances, so we must first check to ensure we are only using QSystemTrayIcon.Trigger.

Reason Value Description

QSystemTrayIcon.Unknown 0 Unknown reason.

QSystemTrayIcon.Context 1 Context menu requested (single click

macOS, right-click Windows).

QSystemTrayIcon.DoubleClick 2 Icon double clicked. On macOS double-

click only fires if no context menu is

set, as the menu opens with a single

click.

QSystemTrayIcon.Trigger 3 Icon clicked once.

QSystemTrayIcon.MiddleClick 4 Icon clicked with the middle mouse

button.

By listening to these events you should be able to construct any type of system tray behavior you wish. However, be sure to check the behavior on all your target platforms.

'PyQt5_' 카테고리의 다른 글

Working with command-line arguments  (0) 2023.03.13
Enums & the Qt Namespace  (0) 2023.03.13
Working with Relative Paths  (0) 2023.03.13
Extending Signals  (0) 2023.03.13
Extending Signals  (0) 2023.03.13

댓글