#!/usr/bin/env python # -*- coding: utf-8 -*- __version__ = '1.0' import signal import sys import atexit import os import time import base64 import stat import pwd import argparse import gi gi.require_version('Gtk', '3.0') gi.require_version('Notify', '0.7') gi.require_version('AppIndicator3', '0.1') from gi.repository import Notify from gi.repository import Gtk from gi.repository import GLib """ -------------------------------------------------------------------------------- Config -------------------------------------------------------------------------------- """ APP_NAME = "Labsync Tray GTK" APP_YEAR = "2019" APP_VERSION = __version__ + '-' + APP_YEAR APP_DESCR = "Notifica lo stato di aggiornamento del software nella cartella /opt" APP_AUTHORS = ["""Sezione di Informatica DiSIT Università del Piemonte Orientale"""] APP_ID = "labsync-tray-gtk" # force using StatusIcon over AppIndicator FORCE_TRAY = False # not used in this version #CURR_DIR = os.path.dirname(os.path.abspath(__file__)) # files do not get deleted on user logout, so better create them in a neutral # location with 666 file permission (see function create_icons_files() below) #TMP_DIR = "/var/tmp/" + pwd.getpwuid(os.getuid()).pw_name TMP_DIR = "/var/tmp/labsync-tray" # state names APP_OFF = 'Off' APP_SYNC = "Syncing" APP_SYNCED = "Synced" # icons: could be PNG or SVG as well APP_ICONS = { APP_OFF: os.path.join(TMP_DIR, 'labsync-off.svg'), APP_SYNCED: os.path.join(TMP_DIR, 'labsync-synced.svg'), APP_SYNC: os.path.join(TMP_DIR, 'labsync-sync.svg') } # seconds between checks CHECK_INTERVAL = 30 CHECK_INTERVAL_UNIT = 's' # seconds, according to previous value # how long the notification stays on the screen (seconds; 0=until user clicks) # WARNING: if NOTIFY_TIME != 0, the application waits anyway when the user close the notification before the time is expired # This is not an issue if we don't care about the status icon menu (GNOME does not show it anymore...) NOTIFY_TIME = 0 # messages MSG_SYNC_TITLE = "labsync" MSG_SYNC_TEXT = """Stiamo aggiornando il software nella cartella /opt; ci \ sarà un po' di attività sul disco.\ \r\rAlcuni dei programmi lì presenti (comprese le macchine virtuali) potrebbero \ non funzionare correttamente fino al termine degli aggiornamenti.\ \r\rIl termine della procedura di aggiornamento ti verrà notificato, ti \ preghiamo di attendere qualche minuto.\ \r\r<b>Se hai atteso più di mezz'ora, non hai ricevuto la notifica e il \ software che vuoi usare continua a non funzionare, contatta i tecnici.</b>""" MSG_SYNC_ICON="dialog-warning" # 0 = normal, 1 = warning, 2 = critical MSG_SYNC_URGENCY = 2 MSG_SYNCED_TITLE = 'labsync' MSG_SYNCED_TEXT = "Il software nella cartella /opt è aggiornato." MSG_SYNCED_ICON="info" # 0 = normal, 1 = warning, 2 = critical MSG_SYNCED_URGENCY = 1 # file to check for saying if we are syncing or not FILE_TO_CHECK = "/opt/ver" # strings STR_MENU_STATUS = "Stato" STR_MENU_ABOUT = "Info" STR_MENU_EXIT = "Esci" STR_BTN_CLOSE = "Chiudi" """ -------------------------------------------------------------------------------- Icons -------------------------------------------------------------------------------- """ APP_ICONS_DATA = { APP_OFF: """\ PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNOTYsMTg4LjE2Yy01MC44OTg1NiwwIC05Mi4xNiwtNDEuMjYxNDQgLTkyLjE2LC05Mi4xNnYwYzAsLTUwLjg5ODU2IDQxLjI2MTQ0LC05Mi4xNiA5Mi4xNiwtOTIuMTZ2MGM1MC44OTg1NiwwIDkyLjE2LDQxLjI2MTQ0IDkyLjE2LDkyLjE2djBjMCw1MC44OTg1NiAtNDEuMjYxNDQsOTIuMTYgLTkyLjE2LDkyLjE2eiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48ZyBpZD0iTGF5ZXJfMSI+PGc+PGc+PGcgZmlsbD0iIzk1YTVhNiI+PGc+PHBhdGggZD0iTTIuOTYyNSw5NmMwLC01MS4zNzUgNDEuNjYyNSwtOTMuMDM3NSA5My4wMzc1LC05My4wMzc1YzUxLjM3NSwwIDkzLjAzNzUsNDEuNjYyNSA5My4wMzc1LDkzLjAzNzVjMCw1MS4zNzUgLTQxLjY2MjUsOTMuMDM3NSAtOTMuMDM3NSw5My4wMzc1Yy01MS4zNzUsMCAtOTMuMDM3NSwtNDEuNjYyNSAtOTMuMDM3NSwtOTMuMDM3NXoiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xNjMuNTc1LDY3LjUzNzVjLTMuODYyNSwwIC03Ljc2MjUsMC4wMzc1IC0xMS42MjUsMC4wMzc1Yy04LjczNzUsLTE5LjU3NSAtMjUuODc1LC0zMy43MTI1IC00Ny40NzUsLTM3LjA4NzVjLTI0LjE1LC0zLjc4NzUgLTQ3LjIxMjUsNy4zODc1IC02MC45NzUsMjcuMTEyNWMtNi4xMTI1LDguNzM3NSA4LjI1LDE2Ljk1IDE0LjMyNSw4LjI1YzE4Ljg2MjUsLTI3IDU5LjE3NSwtMjQuNTYyNSA3NS43MTI1LDEuNzYyNWMtMy42LDAgLTcuMTYyNSwwIC0xMC43NjI1LDAuMDM3NWMtMS4wMTI1LDAgLTEuNzI1LDAuNDUgLTIuMTM3NSwxLjA4NzVjLTAuNzEyNSwwLjc4NzUgLTEuMDEyNSwxLjkxMjUgLTAuMjI1LDMuMDc1YzYuODI1LDEwLjU3NSAxMy42NSwyMS4xODc1IDIwLjQ3NSwzMS43NjI1YzEuMTYyNSwxLjc2MjUgMy42Mzc1LDEuNzYyNSA0Ljc2MjUsMGM2Ljc4NzUsLTEwLjYxMjUgMTMuNTM3NSwtMjEuMjYyNSAyMC4zMjUsLTMxLjg3NWMxLjA4NzUsLTEuOCAtMC40MTI1LC00LjE2MjUgLTIuNCwtNC4xNjI1eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48cGF0aCBkPSJNMTM0LjM2MjUsMTI1LjY2MjVjLTE4LjcxMjUsMjYuODEyNSAtNTguNTM3NSwyNC42IC03NS4zMzc1LC0xLjEyNWMzLjQ1LDAgNi44NjI1LDAgMTAuMzEyNSwtMC4wMzc1YzEuMDEyNSwwIDEuNzI1LC0wLjQ1IDIuMTM3NSwtMS4wODc1YzAuNzEyNSwtMC43ODc1IDEuMDEyNSwtMS45MTI1IDAuMjI1LC0zLjA3NWMtNi44MjUsLTEwLjU3NSAtMTMuNjUsLTIxLjE4NzUgLTIwLjQ3NSwtMzEuNzYyNWMtMS4xNjI1LC0xLjggLTMuNjM3NSwtMS43NjI1IC00Ljc2MjUsMGMtNi43ODc1LDEwLjYxMjUgLTEzLjUzNzUsMjEuMjYyNSAtMjAuMzI1LDMxLjg3NWMtMS4xMjUsMS43NjI1IDAuMzM3NSw0LjEyNSAyLjM2MjUsNC4xMjVjNC4wMTI1LDAgNy45ODc1LC0wLjAzNzUgMTIsLTAuMDM3NWM4LjgxMjUsMTkuMjM3NSAyNS44LDMzLjExMjUgNDcuMjEyNSwzNi40ODc1YzI0LjExMjUsMy43ODc1IDQ3LjE3NSwtNy40MjUgNjAuOTM3NSwtMjcuMTEyNWM2LjExMjUsLTguNzM3NSAtOC4yNSwtMTYuOTEyNSAtMTQuMjg3NSwtOC4yNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxnPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjZTg0ODQ5Ii8+PHBhdGggZD0iTTE0MC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWMtMS4xNzMsLTEuMTczIC0xLjE3MywtMy4wNjkgMCwtNC4yNDJsMjQsLTI0YzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQybC0yNCwyNGMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMTY0LjgsMTY3LjU1NjhjLTAuNzY4LDAgLTEuNTM2LC0wLjI5NCAtMi4xMjEsLTAuODc5bC0yNCwtMjRjLTEuMTczLC0xLjE3MyAtMS4xNzMsLTMuMDY5IDAsLTQuMjQyYzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGwyNCwyNGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQyYy0wLjU4NSwwLjU4NSAtMS4zNTMsMC44NzkgLTIuMTIxLDAuODc5eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4K""", APP_SYNCED: """\ PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiMzNDk4ZGIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjMWZhODViIi8+PHBhdGggZD0iTTE0OS44LDE2Ny41NTY4Yy0wLjc2NzYyLDAgLTEuNTM1MjUsLTAuMjkyODcgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0xLjE3MTg3LC0xLjE3MTg3IC0xLjE3MTg3LC0zLjA3MDUgMCwtNC4yNDIzOGMxLjE3MTg4LC0xLjE3MTg3IDMuMDcwNSwtMS4xNzE4NyA0LjI0MjM3LDBsOS44Nzg2Myw5Ljg3OTM4bDE4Ljg3OSwtMTguODc5YzEuMTcxODgsLTEuMTcxODcgMy4wNzA1LC0xLjE3MTg3IDQuMjQyMzcsMGMxLjE3MTg4LDEuMTcxODggMS4xNzE4OCwzLjA3MDUgMCw0LjI0MjM3bC0yMSwyMWMtMC41ODYxMiwwLjU4NTc1IC0xLjM1Mzc1LDAuODc4NjIgLTIuMTIxMzgsMC44Nzg2MnoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xMTYuOCwxODguNTU2OHYtNzJoNzJ2NzJ6IiBpZD0ib3ZlcmxheS1kcmFnIiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9Im5vbmUiIG9wYWNpdHk9IjAiLz48L2c+PC9nPjwvc3ZnPgo=""", APP_SYNC: """\ PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiNlNjdlMjIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xNTIuOCwxMjIuNTU2OGMxNi41NjYsMCAzMCwxMy40MzQgMzAsMzBjMCwxNi41NjYgLTEzLjQzNCwzMCAtMzAsMzBjLTE2LjU2NiwwIC0zMCwtMTMuNDM0IC0zMCwtMzBjMCwtMTYuNTY2IDEzLjQzNCwtMzAgMzAsLTMwIiBmaWxsPSIjZmZmZmZmIi8+PHBhdGggZD0iTTE2NC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0wLjU2NCwtMC41NjEgLTAuODc5LC0xLjMyNiAtMC44NzksLTIuMTIxdi0yMWMwLC0xLjY1NiAxLjM0NCwtMyAzLC0zYzEuNjU2LDAgMywxLjM0NCAzLDN2MTkuNzU4bDExLjEyMSwxMS4xMjFjMS4xNzMsMS4xNzMgMS4xNzMsMy4wNjkgMCw0LjI0MmMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiMyNmE5ZTAiLz48Y2lyY2xlIGN4PSI0MDcuNDY2NjciIGN5PSI0MDYuODE4MTMiIHRyYW5zZm9ybT0ic2NhbGUoMC4zNzUsMC4zNzUpIiByPSIxMiIgZmlsbD0iIzI2YTllMCIvPjwvZz48L2c+PHBhdGggZD0iTTExNi44LDE4OC41NTY4di03Mmg3MnY3MnoiIGlkPSJvdmVybGF5LWRyYWciIGZpbGw9IiNmZjAwMDAiIHN0cm9rZT0ibm9uZSIgb3BhY2l0eT0iMCIvPjwvZz48L2c+PC9zdmc+Cg==""" } """ -------------------------------------------------------------------------------- Main application class -------------------------------------------------------------------------------- """ class TrayApp(): def __init__(self, appid, icon=None, menu=None, check_interval=None): self.task_changed = True self.task = APP_SYNCED self.current_notification = None self.check_interval = CHECK_INTERVAL # set defaults and parameters if check_interval != None: self.check_interval = check_interval self.menu = self.build_menu() if menu != None: self.menu = menu self.menu.show_all() self.icon = icon if icon != None: self.icon = icon if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False: self.tray = AppIndicator3.Indicator.new( appid, self.icon, AppIndicator3.IndicatorCategory.APPLICATION_STATUS) self.tray.set_status(AppIndicator3.IndicatorStatus.ACTIVE) self.tray.set_menu(self.menu) else: self.tray = Gtk.StatusIcon() self.tray.set_visible(True); self.tray.connect('popup-menu', self.on_popup_menu) self.tray.connect("button-press-event", self.on_mouse_click) self.tray.set_tooltip_text(APP_NAME) self.tray.set_title(APP_NAME) self.tray.set_from_file(self.icon) # initialize task value self.update_task() # intial icon could be different from the current state, force update self.update_icon() # send notification on start only if labsync is syncing if self.task_changed: self.send_notification() # then start updating every CHECK_INTERVAL seconds # http://developer.gnome.org/pygobject/stable/glib-functions.html#function-glib--timeout-add-seconds GLib.timeout_add_seconds(self.check_interval, self.handler_timeout) def on_mouse_click(self, data, event): # 1-left, 2-middle, 3-right if event.button == 1: self.update_task() self.send_notification() # time is required by the popup # time = Gtk.get_current_event_time() #self.menu.popup(None, None, Gtk.StatusIcon.position_menu, self.tray, # event.button, time) def on_popup_menu(self, icon, button, time): self.menu.popup(None, None, Gtk.StatusIcon.position_menu, icon, button, time) def build_menu(self): menu = Gtk.Menu() item_status = Gtk.MenuItem(STR_MENU_STATUS) item_status.connect("activate", self.handler_menu_status) menu.append(item_status) about = Gtk.MenuItem(STR_MENU_ABOUT) about.connect("activate", self.handler_menu_about) menu.append(about) menu.append(Gtk.SeparatorMenuItem()) item_quit = Gtk.MenuItem(STR_MENU_EXIT) item_quit.connect("activate", self.handler_menu_quit) menu.append(item_quit) return menu def handler_menu_quit(self, evt): Gtk.main_quit() def handler_menu_status(self, evt): self.update_task() self.send_notification() def send_notification(self): if self.task == APP_SYNC: title = MSG_SYNC_TITLE txt = MSG_SYNC_TEXT icon = MSG_SYNC_ICON urgency = MSG_SYNC_URGENCY else: title = MSG_SYNCED_TITLE txt = MSG_SYNCED_TEXT icon = MSG_SYNCED_ICON urgency = MSG_SYNCED_URGENCY # close the current notification, if any if self.current_notification != None: self.current_notification.close() self.current_notification = None self.current_notification = Message(None, title, txt, icon, NOTIFY_TIME, urgency).notification self.current_notification.connect("closed", self.handler_current_notification_closed) def handler_current_notification_closed(self, evt): self.current_notification = None def handler_menu_about(self, evt): about_dialog = Gtk.AboutDialog() about_dialog.set_destroy_with_parent(True) about_dialog.set_icon_name("dialog-information") # no SVG? about_dialog.set_logo_icon_name("") about_dialog.set_program_name(APP_NAME) about_dialog.set_name(APP_NAME) about_dialog.set_version(APP_VERSION) about_dialog.set_license_type(Gtk.License.GPL_3_0) about_dialog.set_comments(APP_DESCR) about_dialog.set_authors(APP_AUTHORS) about_dialog.set_default_response(Gtk.ResponseType.CLOSE) about_dialog.run() about_dialog.destroy() def update_icon(self): if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False: self.tray.set_icon(self.icon) else: self.tray.set_from_file(self.icon) def update_task(self): exists = os.path.isfile(FILE_TO_CHECK) if exists: task = APP_SYNCED self.icon = APP_ICONS[APP_SYNCED] else: task = APP_SYNC self.icon = APP_ICONS[APP_SYNC] if self.task != task: self.task_changed = True self.update_icon() else: self.task_changed = False self.task = task def handler_timeout(self): """This will be called every few seconds by the GLib.timeout. """ self.update_task() if self.task_changed: self.send_notification() # return True so that we get called again # returning False will make the timeout stop return True def main(self): Gtk.main() """ -------------------------------------------------------------------------------- Message class -------------------------------------------------------------------------------- """ class Message(): def mess_callback(*args, **kwargs): pass def __init__(self, parent, caption, msg, img=None, timeout=None, urgency=None): self.notification = None if timeout != None: pass else: timeout = 0 # message should not timeout if urgency: pass else: urgency = 0 caps = Notify.get_server_caps() self.notification = Notify.Notification.new(caption, msg, img) # passing an image is optional self.notification.set_timeout(timeout) # milliseconds self.notification.set_urgency(urgency) # 0-Low, 1-Normal, 2-Critical # Without the following `add_action` option, no countdown to the timeout is shown if timeout != 0 and 'actions' in caps: self.notification.add_action("close", STR_BTN_CLOSE, self.mess_callback, None) # Show the countdown to close self.notification.show() # wait (seconds) time.sleep(timeout) self.notification.close() else: self.notification.show() """ -------------------------------------------------------------------------------- Main -------------------------------------------------------------------------------- """ def handler_exit(): cleanup() def handler_signal(sig, frame): cleanup() def initialize(): create_tmp_dir() create_icon_files() def cleanup(): # we can skip files and folder cleanup # the icons are always needed and never cleaned up on session logout... #delete_icon_files() #delete_tmp_dir() Notify.uninit() if Gtk.main_level() > 0: Gtk.main_quit() def create_tmp_dir(): if not os.path.exists(TMP_DIR): os.makedirs(TMP_DIR) # short way to change permissions # see create_icon_files() for alternative way os.chmod(TMP_DIR, 0o777) def delete_tmp_dir(): if os.path.exists(TMP_DIR): os.rmdir(TMP_DIR) def create_icon_files(): for key, value in APP_ICONS.items(): if not os.path.isfile(value): with open(value, 'wb') as fp: fp.write(base64.b64decode(APP_ICONS_DATA[key])) # these 2 command change file permissions to let everyone # delete the files, if needed st = os.stat(APP_ICONS[key]) # https://docs.python.org/3/library/os.html#os.chmod # https://docs.python.org/3/library/stat.html#stat.S_IWUSR os.chmod(APP_ICONS[key], st.st_mode | stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH ) def delete_icon_files(): for key, value in APP_ICONS.items(): if os.path.isfile(value): #print ("Deleted: " + value) os.remove(value) # detect AppIndicator support APPINDICATOR_SUPPORT = 1 try: from gi.repository import AppIndicator3 except: APPINDICATOR_SUPPORT = 0 if __name__ == "__main__": # parse command line arguments parser = argparse.ArgumentParser(description=APP_NAME + ' ' + APP_VERSION) parser.add_argument('-i', '--interval', type=int, help='check interval (seconds)') args = parser.parse_args() initialize() # clean up on exit atexit.register(handler_exit) # normal exit signal.signal(signal.SIGINT, handler_signal) # ctrl-c signal.signal(signal.SIGTERM, handler_signal) # kill signal (but not kill -9) Notify.init(APP_ID) app = TrayApp(APP_ID, icon=APP_ICONS[APP_OFF], check_interval=args.interval) print 'Check interval: ' + str(app.check_interval) + CHECK_INTERVAL_UNIT app.main()