| import unittest | |
| import tkinter | |
| from tkinter.systray import SysTrayIcon, notify | |
| from test.support import requires | |
| from test.test_tkinter.support import (AbstractTkTest, | |
| AbstractDefaultRootTest, | |
| requires_tk, | |
| setUpModule) # noqa: F401 | |
| requires('gui') | |
| class SysTrayIconTest(AbstractTkTest, unittest.TestCase): | |
| def setUp(self): | |
| super().setUp() | |
| self.image = tkinter.PhotoImage(master=self.root, | |
| width=16, height=16) | |
| def create(self, **kwargs): | |
| try: | |
| icon = SysTrayIcon(self.root, image=self.image, **kwargs) | |
| except tkinter.TclError as e: | |
| self.skipTest(f'cannot create a system tray icon: {e}') | |
| self.addCleanup(self._destroy, icon) | |
| return icon | |
| def _destroy(self, icon): | |
| try: | |
| icon.destroy() | |
| except tkinter.TclError: | |
| pass | |
| @requires_tk(8, 7) | |
| def test_create(self): | |
| icon = self.create(text='tooltip') | |
| self.assertTrue(icon.exists()) | |
| self.assertEqual(str(icon.cget('image')), str(self.image)) | |
| self.assertEqual(icon.cget('text'), 'tooltip') | |
| @requires_tk(8, 7) | |
| def test_create_requires_image(self): | |
| with self.assertRaises(TypeError): | |
| SysTrayIcon(self.root) | |
| @requires_tk(8, 7) | |
| def test_exists_argument(self): | |
| icon = self.create(text='tooltip') | |
| # exists=True refers to the already-created icon without creating | |
| # a new one (which would raise the singleton error). | |
| icon2 = SysTrayIcon(self.root, exists=True) | |
| self.assertTrue(icon2.exists()) | |
| self.assertEqual(icon2.cget('text'), 'tooltip') | |
| # It can reconfigure the existing icon. | |
| SysTrayIcon(self.root, exists=True, text='new') | |
| self.assertEqual(icon.cget('text'), 'new') | |
| @requires_tk(8, 7) | |
| def test_singleton(self): | |
| self.create() | |
| with self.assertRaisesRegex(tkinter.TclError, | |
| 'only one system tray icon'): | |
| SysTrayIcon(self.root, image=self.image) | |
| @requires_tk(8, 7) | |
| def test_configure(self): | |
| icon = self.create(text='old') | |
| icon.configure(text='new') | |
| self.assertEqual(icon.cget('text'), 'new') | |
| options = icon.configure() | |
| self.assertIsInstance(options, dict) | |
| self.assertLessEqual({'image', 'text', 'button1', 'button3'}, | |
| options.keys()) | |
| @requires_tk(8, 7) | |
| def test_callbacks(self): | |
| clicks = [] | |
| icon = self.create(button1=lambda: clicks.append(1)) | |
| name = icon._command_names['button1'] | |
| # The registered callback is called without arguments. | |
| self.root.tk.call(name) | |
| self.assertEqual(clicks, [1]) | |
| # Replacing the callback deletes the old Tcl command. | |
| icon.configure(button1=lambda: clicks.append(2)) | |
| self.assertFalse(self.root.tk.call('info', 'commands', name)) | |
| self.root.tk.call(icon._command_names['button1']) | |
| self.assertEqual(clicks, [1, 2]) | |
| # Removing the callback deletes the Tcl command. | |
| name = icon._command_names['button1'] | |
| icon.configure(button1=None) | |
| self.assertNotIn('button1', icon._command_names) | |
| self.assertFalse(self.root.tk.call('info', 'commands', name)) | |
| @requires_tk(8, 7) | |
| def test_destroy(self): | |
| icon = self.create(button1=lambda: None) | |
| name = icon._command_names['button1'] | |
| icon.destroy() | |
| self.assertFalse(icon.exists()) | |
| self.assertFalse(self.root.tk.call('info', 'commands', name)) | |
| # A new icon can be created after the old one was destroyed. | |
| icon2 = self.create() | |
| self.assertTrue(icon2.exists()) | |
| @requires_tk(8, 7) | |
| def test_notify(self): | |
| if self.root._windowingsystem != 'x11': | |
| self.skipTest('cannot safely send a native notification') | |
| icon = self.create() | |
| # Sends a real desktop notification. | |
| icon.notify('Python test', 'tkinter.systray test notification') | |
| class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): | |
| @requires_tk(8, 7) | |
| def test_systray(self): | |
| root = tkinter.Tk() | |
| image = tkinter.PhotoImage(master=root, width=16, height=16) | |
| try: | |
| icon = SysTrayIcon(image=image) | |
| except tkinter.TclError as e: | |
| root.destroy() | |
| self.skipTest(f'cannot create a system tray icon: {e}') | |
| self.assertIs(icon.master, root) | |
| icon.destroy() | |
| root.destroy() | |
| tkinter.NoDefaultRoot() | |
| self.assertRaises(RuntimeError, SysTrayIcon, image='none') | |
| self.assertRaises(RuntimeError, notify, 'title', 'message') | |
| if __name__ == "__main__": | |
| unittest.main() |