def run(self): def get_wrapper(command): def wrapper(): self.r_queue.put((command, None)) return wrapper def die(): self.icon_stop() menu_items = [ MenuItem("Show Console", get_wrapper("show_console")), MenuItem("Application Menu", get_wrapper("open_player_menu")), MenuItem("Open Config Folder", get_wrapper("open_config_brs")), MenuItem("Quit", die) ] icon = Icon(APP_NAME, menu=Menu(*menu_items)) icon.icon = Image.open(icon_file) self.icon_stop = icon.stop def setup(icon: Icon): icon.visible = True icon.run(setup=setup) self.r_queue.put(("die", None))
def run(self): from pystray import Icon, MenuItem, Menu def get_wrapper(command): def wrapper(): self.r_queue.put((command, None)) return wrapper def die(): # We don't call self.icon_stop() because it crashes on Linux now... if sys.platform == "linux": # This kills the status icon uncleanly. self.r_queue.put(("die", None)) else: self.icon_stop() menu_items = [ MenuItem(_("Configure Servers"), get_wrapper("show_preferences")), MenuItem(_("Show Console"), get_wrapper("show_console")), MenuItem(_("Application Menu"), get_wrapper("open_player_menu")), MenuItem(_("Open Config Folder"), get_wrapper("open_config_brs")), MenuItem(_("Quit"), die) ] icon = Icon(USER_APP_NAME, menu=Menu(*menu_items)) icon.icon = Image.open(get_resource("systray.png")) self.icon_stop = icon.stop def setup(icon): icon.visible = True self.r_queue.put(("ready", None)) icon.run(setup=setup) self.r_queue.put(("die", None))
def tray(): global icon, thread icon = None thread = None name = 'ED - Autopilot' icon = Icon(name=name, title=name) logo = Image.open(resource_path('src/logo.png')) icon.icon = logo icon.menu = Menu( MenuItem('Scan Off', set_state(0), checked=get_state(0), radio=True), MenuItem('Scan on Primary Fire', set_state(1), checked=get_state(1), radio=True), MenuItem('Scan on Secondary Fire', set_state(2), checked=get_state(2), radio=True), MenuItem('Exit', lambda: exit_action())) keyboard.add_hotkey('home', start_action) keyboard.add_hotkey('end', stop_action) icon.run(setup)
def run(self): from pystray import Icon, MenuItem, Menu def get_wrapper(command): def wrapper(): self.r_queue.put((command, None)) return wrapper def die(): self.icon_stop() menu_items = [ MenuItem("Configure Servers", get_wrapper("show_preferences")), MenuItem("Show Console", get_wrapper("show_console")), MenuItem("Application Menu", get_wrapper("open_player_menu")), MenuItem("Open Config Folder", get_wrapper("open_config_brs")), MenuItem("Quit", die) ] icon = Icon(USER_APP_NAME, menu=Menu(*menu_items)) with importlib.resources.path(__package__, 'systray.png') as icon_file: icon.icon = Image.open(icon_file) self.icon_stop = icon.stop def setup(icon): icon.visible = True self.r_queue.put(("ready", None)) icon.run(setup=setup) self.r_queue.put(("die", None))
class SystemTray: #Initialises the system tray def __init__(self): #system tray image loading image = Image.open('icon.ico') image.load() #Makes a menu for the system tray icon menu = Menu( MenuItem('Start Recognition', self.onStartRecognition), MenuItem('Stop Recognition', self.onStopRecognition), MenuItem('Exit', self.exitProgram)) #Creates the icon and makes it visible self.icon = Icon(name='Speech Transcriber', title='Speech Transcriber', icon=image, menu=menu) self.icon.visible = True #Starts the system tray def startSysTray(self): self.icon.run() #Starts recognition when the start button is clicked on the menu def onStartRecognition(self, icon): self.startspeech print("start") #Stops recognition when the stop button is clicked on the menu def onStopRecognition(self, icon): print("stop") #Closes the program when the quit button is clicked in the menu def exitProgram(self, icon): self.icon.stop()
def on_close(): root.withdraw() global systray, tray_running my_menu = Menu(MenuItem("Open", open_window), MenuItem("Quit", quit_program)) systray = Icon(app_name, Image.open(resource_path('icon.ico')), menu=my_menu) tray_running = True systray.run()
def tray(): global ICON name = 'ED - Autopilot' ICON = Icon(name=name, title=name) logo = Image.open(join(abspath("."), 'src/logo.png')) ICON.icon = logo keyboard.add_hotkey('page up', start_action) keyboard.add_hotkey('page down', stop_action) keyboard.add_hotkey('end', kill_ed) # keyboard.wait() ICON.run(setup)
def run(self): menu_items = [ MenuItem("Configure Servers", self.show_preferences), MenuItem("Show Console", self.show_console), MenuItem("Application Menu", self.open_player_menu), ] if open_config: menu_items.append(MenuItem("Open Config Folder", open_config)) menu_items.append(MenuItem("Quit", self.stop)) icon = Icon(USER_APP_NAME, menu=Menu(*menu_items)) icon.icon = Image.open(icon_file) self.icon_stop = icon.stop icon.run()
class Tray: def __init__(self, image, name, cmd): self.cmd = cmd self.icon = Icon(name, image, menu=Menu(MenuItem('Quit', self.quit))) def run(self): self.process = popen_and_call(self.on_quit, shlex.split(self.cmd)) self.icon.run() def on_quit(self): self.icon.stop() def quit(self): self.process.send_signal(signal.SIGINT)
def traymenu(): global RUN try: image = Image.open("icon.png") menu = ( item('Run', on_clicked, checked=lambda item:RUN), item("Settings", settingsmenu), item("About", about), item('Quit', quit) ) icon = pystray.Icon("name", image,"title",menu) icon.run() except: error_message("traymenu failed")
class SysTrayUI: def __init__(self): self.cs = CapSwitch() #Run CapSwitch when it is opened self.init_thread(target=self.cs.start_listener) # *** Attributes *** self.icon_path = "icon.png" # *** Setup Menu *** self.menu_items = [ MenuItem(self.check_status, self.toggle), MenuItem("Exit", self.exit), ] self.menu = Menu(*self.menu_items) # *** Create System Tray Icon *** self.icon = Icon("CapSwitch", menu=self.menu) self.icon.icon = Image.open(self.icon_path) self.icon.run() def toggle(self, *args): if not self.cs.running: self.init_thread(target=self.cs.start_listener) else: c = Controller() with c.pressed(Key.pause): with c.pressed(Key.end): c.press(Key.down) c.release(Key.down) def check_status(self, *args): name = "CapSwitch" if self.cs.running: return f"Disable {name}" else: return f"Enable {name}" def init_thread(self, target, *args): new_thread = threading.Thread(target=target) new_thread.start() def exit(self, *args): self.icon.stop() if self.cs.running: self.toggle()
def tray(): global icon, thread icon = None thread = None name = 'ED - Autopilot' icon = Icon(name=name, title=name) logo = Image.open(resource_path('src/logo.png')) icon.icon = logo icon.menu = Menu(MenuItem('Exit', lambda: exit_action()), ) keyboard.add_hotkey('home', start_action) keyboard.add_hotkey('end', stop_action) icon.run(setup)
def configure_systray(): def setup(icon): icon.visible = True def exit_action(icon): icon.visible = False icon.stop() os._exit(1) image = Image.open(os.path.join("images", "snappy_systray.png")) icon = Icon("snappy", icon=image, title="snappy") menu = Menu(MenuItem("exit", lambda: exit_action(icon))) icon.menu = menu icon.run(setup)
def main(): logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s', level=logging.INFO) logging.info('URL Trimmer boot!') global thread thread = ClipboardThread(WindowsTextClipboard(), DEFAULT_FILTERS) logging.info('Starting clipboard thread') thread.start() image = Image.open('icon.png') icon = Icon('URL Trimmer', icon=image, menu=Menu( MenuItem('Settings', lambda icon, item: None), MenuItem('Exit', close_app) )) logging.info('Starting system tray icon') icon.run()
def start(self): def on_clicked(icon, _): logging.info('[APP] Closing. Source: TrayIcon') states.running = False icon.stop() def clicked_display(_, item): states.displayed = item.checked tray_icon = Icon('chesscord', title='chesscord', icon=Image.open(RESOURCES_DIR / 'logo.png')) tray_icon.menu = Menu( MenuItem('update config', utils.open_webpage), MenuItem('hide', clicked_display, checked=lambda item: not states.displayed), MenuItem('Exit', on_clicked)) tray_icon.run()
def tray(): global icon, thread icon = None thread = None name = 'ED - Autopilot' icon = Icon(name=name, title=name) logo = Image.open(resource_path('src/logo.png')) icon.icon = logo icon.menu = Menu( MenuItem( 'Scan Off', set_state(0), checked=get_state(0), radio=True ), MenuItem( 'Scan on Primary Fire', set_state(1), checked=get_state(1), radio=True ), MenuItem( 'Scan on Secondary Fire', set_state(2), checked=get_state(2), radio=True ), MenuItem('Exit', lambda : exit_action()) ) keyboard.add_hotkey('home', start_action) keyboard.add_hotkey('end', stop_action) icon.run(setup)
from pystray import Icon as icon, Menu as menu, MenuItem as item from PIL import Image, ImageDraw from json import loads as json_loads, dumps as json_dumps import app def create_image(): image = Image.open('./icon.png') image.thumbnail([64, 64], Image.ANTIALIAS) return image def set_scene(icon, item): app.scene(str(item)) icon = icon( 'test', create_image(), menu=menu( item('on', lambda icon, item: app.on()), item('off', lambda icon, item: app.off()), item( 'scenes', menu(lambda: (item(scene, set_scene) for scene in app.CONFIGS['scenes'].keys()))))) icon.run()
class TrayIcon: def __init__(self, path_to_image: Path = image_path): # Silence PIL noisy logging logging.getLogger('PIL.PngImagePlugin').setLevel(logging.INFO) logging.getLogger('PIL.Image').setLevel(logging.INFO) self.path_to_image: Path = path_to_image self.icon: Optional['Icon'] = None self._menu: Optional['Menu'] = None self.menu_items: List['MenuItem'] = [] self.active: bool = _import_success self.running: bool = False self.add_core_menu_items() @check_if_tray_is_active def add_menu_item( self, text: str = None, action: Callable = None, menu_item: 'MenuItem' = None, index: int = None, **kwargs, ): """ Add a menu item byt passing its text and function, or pass a created MenuItem. Force position by sending index """ if not any(v for v in (menu_item, text)): raise ValueError(f"Either 'text' or 'menu_item' are required") menu_item = menu_item or MenuItem(text=text, action=action, **kwargs) if index is not None: self.menu_items.insert(index, menu_item) else: self.menu_items.append(menu_item) @check_if_tray_is_active def add_menu_separator(self, index: int = None): self.add_menu_item(menu_item=Menu.SEPARATOR, index=index) def add_core_menu_items(self): open_web = partial(webbrowser.open) self.add_menu_item(text=f'Flexget {__version__}', enabled=False) self.add_menu_separator() self.add_menu_item(text='Homepage', action=partial(open_web, 'https://flexget.com/')) self.add_menu_item(text='Forum', action=partial(open_web, 'https://discuss.flexget.com/')) @property def menu(self) -> 'Menu': # This is lazy loaded since we'd like to delay the menu build until the tray is requested to run if not self._menu: self._menu = Menu(*self.menu_items) return self._menu @check_if_tray_is_active def run(self): """Run the tray icon. Must be run from the main thread and is blocking""" try: logger.verbose('Starting tray icon') self.icon = Icon('Flexget', Image.open(self.path_to_image), menu=self.menu) self.running = True self.icon.run() except Exception as e: logger.warning('Could not run tray icon: {}', e) self.running = False @check_if_tray_is_active def stop(self): if not self.running: return logger.verbose('Stopping tray icon') self.icon.stop() self.running = False
class Tray(): def __init__(self, app_name): self.app_name = app_name self.ALGORITHM_STATE = Config.CURRENT_ALGORITHM self.icon = Icon(self.app_name, icon=self.get_icon(), menu=menu( item('Show QR code', lambda icon: self.onclick_qr()), item('Open Results Dir', lambda icon: self.onclick_results()), menu.SEPARATOR, item('Matching Algorithm', lambda icon: None, enabled=False), item('SURF (Precise)', self.set_state('SURF'), checked=self.get_state('SURF'), radio=True), item('ORB (Fast)', self.set_state('ORB'), checked=self.get_state('ORB'), radio=True), menu.SEPARATOR, item('Quit', lambda icon: self.onclick_quit()))) def set_state(self, v): def inner(icon, item): self.ALGORITHM_STATE = v print('Switching Algorithm to %s' % self.ALGORITHM_STATE) Config.CURRENT_ALGORITHM = self.ALGORITHM_STATE return inner def get_state(self, v): def inner(item): return self.ALGORITHM_STATE == v return inner def get_icon(self): return Image.open( BytesIO(base64.b64decode(self.get_branding_datauri()))) def run(self): self.icon.run(self.setup) def onclick_quit(self): self.icon.stop() def onclick_qr(self): if Config.IS_DIST: os.system('show_qr "{}"'.format(Config.SERVICE_URL)) else: if platform.system() == 'Linux': os.system('python3 show_qr.py "{}"'.format(Config.SERVICE_URL)) else: proc_result = subprocess.run('show_qr.py "{}"'.format( Config.SERVICE_URL), shell=True) if proc_result.returncode != 0: proc_result = subprocess.run('show_qr.bat "{}"'.format( Config.SERVICE_URL), shell=True) def onclick_results(self): if Config.IS_DIST: common.utils.open_file_or_dir('./www') else: common.utils.open_file_or_dir( common.utils.getScriptDir(__file__) + '/../www/results') def setup(self, icon): self.icon.visible = True def get_branding_datauri(self): return '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AAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINABAAAggEAHAACAAAIdAAAAAgh0AAAACCDQAQAAIIBABwAAgAACHQAAAAIIdAAAAAgg0AEAACCAQAcAAIAAAh0AAAACCHQAAAAIINAB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'
print('You Pressed A Key!') if stop(): break # finishing the loop except: if stop(): break # if user pressed a key other than the given key the loop will break except: pass # Release handle to the webcam once the while loop is broken video_capture.release() cv2.destroyAllWindows() icon.stop() # Sys tray icon setup # Set base image (needs changing to blank white square) image = Image.open( icon_location) # Change this file path to match your icon files location # Add menu options menu = (item('view', lambda: view()), item('pause/start', lambda: pause_loop()), item('close', lambda: stop())) # Set sys tray icon parameters icon = pystray.Icon("Test Icon 1", image, "Facial Lock", menu) # Build sys tray icon icon.visible = True icon.run(setup=callback) # Stop sys tray icon
class SystrayIcon(object): def __init__(self, worker: Worker): # save worker reference self._worker = worker # create the icon self._icon = Icon("Mover") # set the image self._icon.icon = MOVER_ICON # set actions # - set source # - set destination # - number of backlogs # - start/stop # - exit self._start_stop = MenuItem("Start", self.on_start_stop, enabled=False) # initial state self._set_source = MenuItem("Set Source", self.on_set_source) self._set_destination = MenuItem("Set Destination", self.on_set_destination) self._number_of_backlogs = MenuItem( "Number of Backlogs", Menu( MenuItem("Immediate", partial(self.on_number_of_backlogs_changed, 0)), MenuItem("5", partial(self.on_number_of_backlogs_changed, 5)), MenuItem("10", partial(self.on_number_of_backlogs_changed, 10)), MenuItem("25", partial(self.on_number_of_backlogs_changed, 25)), ), ) self._timeout = MenuItem( 'Timeout', Menu(MenuItem('Never', partial(self.on_timeout_changed, 0)), MenuItem('30 s', partial(self.on_timeout_changed, 30)))) self._exit = MenuItem("Exit", self.on_exit) self._icon.menu = Menu( self._start_stop, Menu.SEPARATOR, self._set_source, self._set_destination, self._number_of_backlogs, Menu.SEPARATOR, self._exit, ) ## def start(self): self._icon.run() def stop(self): self._icon.stop() ## def on_start_stop(self): # toggle state if self._worker.is_running: self._worker.stop() else: self._worker.start() def on_set_source(self): path = self._ask_folder_path("Please select the source directory") #self._worker.set_source(path) self._update_start_stop_state() def on_set_destination(self): path = self._ask_folder_path("Please select the destination directory") self._worker.set_destination(path) self._update_start_stop_state() def on_number_of_backlogs_changed(self, n): self._worker.set_number_of_backlogs(n) def on_timeout_changed(self, t): self._worker.stop() self._worker = Worker(t) def on_exit(self): logger.debug(f"systray stopped") self.stop() ## def _update_start_stop_state(self): self._start_stop.enabled = self._worker.can_run self._icon.update_menu() def _ask_folder_path(self, title="Please select a directory"): root = tk.Tk() root.withdraw() return filedialog.askdirectory(title=title)
class SysTray: """ systray icon using pystray package """ def __init__(self, main_window): self.main_window = main_window self._tray_icon_path = os.path.join(config.sett_folder, 'systray.png') # path to icon self.icon = None self._hover_text = None self.Gtk = None self.active = False def show_main_window(self, *args): self.main_window.unhide() def minimize_to_systray(self, *args): self.main_window.hide() @property def tray_icon(self): """return pillow image""" try: img = atk.create_pil_image(b64=APP_ICON2) return img except Exception as e: log('systray: tray_icon', e) if config.TEST_MODE: raise e @property def tray_icon_path(self): # save icon as a png on setting directory and return path if not os.path.isfile(self._tray_icon_path): try: # save file to settings folder self.tray_icon.save(self._tray_icon_path, format='png') except: pass return self._tray_icon_path def run(self): # not supported on mac if config.operating_system == 'Darwin': log('Systray is not supported on mac yet') return options_map = { 'Show': self.show_main_window, 'Minimize to Systray': self.minimize_to_systray, 'Quit': self.quit } # make our own Gtk statusIcon, since pystray failed to run icon properly on Gtk 3.0 from a thread if config.operating_system == 'Linux': try: import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk self.Gtk = Gtk def icon_right_click(icon, button, time): menu = Gtk.Menu() for option, callback in options_map.items(): item = Gtk.MenuItem(label=option) item.connect('activate', callback) menu.append(item) menu.show_all() menu.popup(None, None, None, icon, button, time) icon = Gtk.StatusIcon() icon.set_from_file(self.tray_icon_path) icon.connect("popup-menu", icon_right_click) icon.connect('activate', self.show_main_window) self.active = True Gtk.main() return except: self.active = False # let pystray decide which icon to run try: from pystray import Icon, Menu, MenuItem items = [] for option, callback in options_map.items(): items.append( MenuItem(option, callback, default=True if option == 'Show' else False)) menu = Menu(*items) self.icon = Icon('PyIDM', self.tray_icon, menu=menu) self.active = True self.icon.run() except Exception as e: log('systray: - run() - ', e) self.active = False def shutdown(self): try: self.active = False self.icon.stop() except: pass try: # quit main, don't know why it raise (Gtk-CRITICAL **:gtk_main_quit: assertion 'main_loops != NULL' failed) # but it has no side effect and PyIDM quit normally self.Gtk.main_quit() except: pass def quit(self, *args): """callback when selecting quit from systray menu""" self.shutdown() self.main_window.close()
_items.extend([ MenuItem("", action=void), MenuItem( text= "Pull changes for fast forwardable repositories marked with [FF]", action=pull_changes_for_list_of_repos(repos), ), ]) if was_up_to_date: n = Notifier() n.send_notification(output + wrong_output) if wrong_paths: _items.extend( [MenuItem(f"{text}", action=void) for text in wrong_output]) _menu = Menu(*_items) icon.icon = _icon icon.menu = _menu time.sleep(args.delay) menu = Menu(MenuItem(text="Checking repositories...", action=void)) icon = Icon(name=MESSAGE, icon=icon_loading, menu=menu) icon.run(setup)
class Updater: def __init__(self, options={}): self.__main_gui = None self.__notifier = sh.Command("/usr/bin/notify-send") self.__running = True self.__updater = threading.Thread(target=self.__update_loop) self.__scheduler = sched.scheduler(time.time, time.sleep) self.__config = config.AppConfig('dnfdragora') self.__updateInterval = 180 self.__update_count = -1 if self.__config.systemSettings: settings = {} if 'settings' in self.__config.systemSettings.keys(): settings = self.__config.systemSettings['settings'] if 'update_interval' in settings.keys(): self.__updateInterval = int(settings['update_interval']) if self.__config.userPreferences: if 'settings' in self.__config.userPreferences.keys(): settings = self.__config.userPreferences['settings'] if 'interval for checking updates' in settings.keys(): self.__updateInterval = int( settings['interval for checking updates']) icon_path = '/usr/share/icons/hicolor/128x128/apps' if 'icon-path' in options.keys(): icon_path = options['icon-path'] if icon_path.endswith('/'): icon_path = icon_path + 'dnfdragora.png' else: icon_path = icon_path + '/dnfdragora.png' try: from gi.repository import Gtk icon_theme = Gtk.IconTheme.get_default() icon_path = icon_theme.lookup_icon("dnfdragora", 128, 0).get_filename() except: pass print("icon %s" % (icon_path)) self.__icon = Image.open(icon_path) self.__menu = Menu( MenuItem(_('Update'), self.__run_update), MenuItem(_('Open dnfdragora dialog'), self.__run_dnfdragora), MenuItem(_('Check for updates'), self.__get_updates_forced), MenuItem(_('Exit'), self.__shutdown)) self.__name = 'dnfdragora-updater' self.__tray = Tray(self.__name, self.__icon, self.__name, self.__menu) def __shutdown(self, *kwargs): print("shutdown") if self.__main_gui: print("----> %s" % ("RUN" if self.__main_gui.running else "NOT RUNNING")) return try: self.__running = False self.__updater.join() try: if self.__backend: self.__backend.Unlock() self.__backend.Exit() except: pass yui.YDialog.deleteAllDialogs() yui.YUILoader.deleteUI() except: pass finally: if self.__scheduler.empty() != False: for task in self.__scheduler.queue: try: self.__scheduler.cancel(task) except: pass if self.__tray != None: self.__tray.stop() if self.__backend: self.__backend.Exit() def __run_dialog(self, args, *kwargs): if self.__tray != None and self.__main_gui == None: time.sleep(0.5) try: self.__main_gui = ui.mainGui(args) except Exception as e: dialogs.warningMsgBox({ 'title': _("Running dnfdragora failure"), "text": str(e), "richtext": True }) yui.YDialog.deleteAllDialogs() time.sleep(0.5) self.__main_gui = None return self.__tray.icon = None self.__main_gui.handleevent() while self.__main_gui.loop_has_finished != True: time.sleep(1) yui.YDialog.deleteAllDialogs() time.sleep(1) self.__main_gui = None self.__get_updates() def __run_dnfdragora(self, *kwargs): return self.__run_dialog({}) def __run_update(self, *kwargs): return self.__run_dialog({'update_only': True}) def __get_updates(self, *kwargs): return self.__get_updates_func(False) def __get_updates_forced(self, *kwargs): return self.__get_updates_func(True) def __get_updates_func(self, forced, *kwargs): try: self.__backend = dnfdaemon.client.Client() except dnfdaemon.client.DaemonError as error: print(_('Error starting dnfdaemon service: [%s]') % (str(error))) self.__update_count = -1 self.__tray.icon = None return except Exception as e: print(_('Error starting dnfdaemon service: [%s]') % (str(e))) self.__update_count = -1 self.__tray.icon = None return try: if self.__backend.Lock(): pkgs = self.__backend.GetPackages('updates') self.__update_count = len(pkgs) self.__backend.Unlock() self.__backend.Exit() time.sleep(0.5) self.__backend = None if (self.__update_count >= 1) or forced: self.__notifier( '-a', 'dnfdragora-updater', '-i', 'dnfdragora', '-u', 'normal', 'dnfdragora', _('%d updates available.') % self.__update_count) self.__tray.icon = self.__icon self.__tray.visible = True else: print("DNF backend already locked cannot check for updates") self.__update_count = -1 self.__tray.icon = None self.__backend = None except Exception as e: print(_('Exception caught: [%s]') % (str(e))) def __update_loop(self): self.__get_updates() while self.__running == True: if self.__scheduler.empty(): self.__scheduler.enter(self.__updateInterval * 60, 1, self.__get_updates) self.__scheduler.run(blocking=False) time.sleep(1) def __main_loop(self): def setup(tray): tray.visible = False self.__updater.start() time.sleep(1) self.__tray.run(setup=setup) print("dnfdragora-updater termination") def main(self): self.__main_loop()
class Updater: def __init__(self): self.__main_gui = None self.__notifier = sh.Command("/usr/bin/notify-send") self.__running = True self.__updater = threading.Thread(target=self.__update_loop) self.__scheduler = sched.scheduler(time.time, time.sleep) self.__config = config.AppConfig('dnfdragora') self.__updateInterval = 180 if self.__config.systemSettings: settings = {} if 'settings' in self.__config.systemSettings.keys(): settings = self.__config.systemSettings['settings'] if 'update_interval' in settings.keys(): self.__updateInterval = int(settings['update_interval']) if self.__config.userPreferences: if 'settings' in self.__config.userPreferences.keys(): settings = self.__config.userPreferences['settings'] if 'interval for checking updates' in settings.keys(): self.__updateInterval = int( settings['interval for checking updates']) icon_path = '/usr/share/icons/hicolor/128x128/apps/dnfdragora.png' try: self.__backend = dnfdaemon.client.Client() except dnfdaemon.client.DaemonError as error: print(_('Error starting dnfdaemon service: [%s]'), str(error)) sys.exit(1) try: from gi.repository import Gtk icon_theme = Gtk.IconTheme.get_default() icon_path = icon_theme.lookup_icon("dnfdragora", 128, 0).get_filename() except: pass self.__icon = Image.open(icon_path) self.__menu = Menu( MenuItem(_('Update'), self.__run_update), MenuItem(_('Open dnfdragora dialog'), self.__run_dnfdragora), MenuItem(_('Check for updates'), self.__get_updates_forced), MenuItem(_('Exit'), self.__shutdown)) self.__name = 'dnfdragora-updater' self.__tray = Tray(self.__name, self.__icon, self.__name, self.__menu) def __shutdown(self, *kwargs): try: self.__running = False self.__updater.join() self.__main_gui.quit() while self.__main_gui.loop_has_finished != True: time.sleep(1) try: self.__backend.Unlock() self.__main_gui.backend.quit() except: pass yui.YDialog.deleteAllDialogs() yui.YUILoader.deleteUI() except: pass finally: if self.__scheduler.empty() != False: for task in self.__scheduler.queue(): try: self.__scheduler.cancel(task) except: pass self.__tray.stop() self.__backend.Exit() def __run_dialog(self, args, *kwargs): if self.__tray != None: self.__main_gui = ui.mainGui(args) self.__main_gui.handleevent() def __run_dnfdragora(self, *kwargs): return self.__run_dialog({}) def __run_update(self, *kwargs): return self.__run_dialog({'update_only': True}) def __get_updates(self, *kwargs): return self.__get_updates_func(False) def __get_updates_forced(self, *kwargs): return self.__get_updates_func(True) def __get_updates_func(self, forced, *kwargs): if self.__backend.Lock(): pkgs = self.__backend.GetPackages('updates') update_count = len(pkgs) self.__backend.Unlock() if (update_count >= 1) or forced: self.__notifier('-a', 'dnfdragora-updater', '-i', 'dnfdragora', '-u', 'normal', 'dnfdragora', _('%d updates available.') % update_count) else: update_count = -1 def __update_loop(self): while self.__running == True: if self.__scheduler.empty(): self.__scheduler.enter(self.__updateInterval * 60, 1, self.__get_updates) self.__scheduler.run(blocking=False) time.sleep(1) def __main_loop(self): self.__tray.visible = True self.__get_updates() self.__updater.start() def main(self): self.__tray.run(self.__main_loop())
root_folder.open() return manager.set_on_connected_callback(on_connected_callback) manager.listen_connections() def test_button(): print("test button") def open_ui_button(): print("opening ui") ui.focus_or_create_ui() return def quit_button(): print("quitting") os._exit(0) image = Image.open("icon.png") tray_app = Icon('test', image, menu=Menu( MenuItem('Test', test_button), MenuItem('Open UI', open_ui_button, default=True), MenuItem('Quit', quit_button))) tray_app.run()
# Called after icon is set up due to threading issues. spotify_helper = SpotifyHelper() def run_spotify_helper(icon_callback): try: icon_callback.visible = True spotify_helper.run() except Exception as e: logging.error('{}:{}'.format(e, traceback.format_exc())) traceback.print_exc() def stop_program(icon_callback): icon_callback.stop() spotify_helper.stop() icon_image = Image.open( os.path.join(os.path.dirname(__file__), 'resources/spo.png')) icon = Icon('spotify-helper', icon_image, menu=Menu( MenuItem(text='Edit bindings', action=open_bindings_file), Menu.SEPARATOR, MenuItem(text='Quit', action=lambda: stop_program(icon)), )) # After icon starts running, we start the keyboard listener thread (together with the main # Spotify Helper code), since on macOS pystray won't work if pynput runs first, as the latter seems # to call a Mac _MainThread function which pystray then tries to call again but is not allowed - running # pystray first seems to be ok though. icon.run(setup=run_spotify_helper)
image = Image.new('RGB', (width, height)) dc = ImageDraw.Draw(image) dc.ellipse((0, 0, 120, 120), fill='red') dc.ellipse((20, 20, 100, 100), fill='blue') dc.ellipse((40, 40, 80, 80), fill='green') def turnOnOrOff(icon, item): global ambientOn ambientOn = not item.checked if ambientOn: ambientOnEvent.set() else: ambientOnEvent.clear() icon = Icon( "Ambient RGB Light", image, "Ambient RGB Light", Menu( MenuItem('Ambient On', action=turnOnOrOff, checked=lambda item: ambientOn))) def iconCallback(icon): icon.visible = True main() icon.run(iconCallback)
class SysTray: """ systray icon using pystray package """ def __init__(self, main_window): self.main_window = main_window self.tray_icon_path = os.path.join(config.sett_folder, 'systray.png') # path to icon self.icon = None self._hover_text = None self.Gtk = None self.active = False def show_main_window(self, *args): # unhide and bring on top self.main_window.focus() def minimize_to_systray(self, *args): self.main_window.hide() @property def tray_icon(self): """return pillow image""" try: img = atk.create_pil_image(b64=APP_ICON, size=48) return img except Exception as e: log('systray: tray_icon', e) if config.test_mode: raise e def run(self): # not supported on mac if config.operating_system == 'Darwin': log('Systray is not supported on mac yet') return options_map = {'Show': self.show_main_window, 'Minimize to Systray': self.minimize_to_systray, 'Quit': self.quit} # make our own Gtk statusIcon, since pystray failed to run icon properly on Gtk 3.0 from a thread if config.operating_system == 'Linux': try: import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk self.Gtk = Gtk # delete previous icon file (it might contains an icon file for old firedm versions) delete_file(self.tray_icon_path) # save file to settings folder self.tray_icon.save(self.tray_icon_path, format='png') # creating menu menu = Gtk.Menu() for option, callback in options_map.items(): item = Gtk.MenuItem(label=option) item.connect('activate', callback) menu.append(item) menu.show_all() APPINDICATOR_ID = config.APP_NAME # setup notify system, will be used in self.notify() gi.require_version('Notify', '0.7') from gi.repository import Notify as notify self.Gtk_notify = notify # get reference for later deinitialize when quit systray self.Gtk_notify.init(APPINDICATOR_ID) # initialize first # try appindicator try: gi.require_version('AppIndicator3', '0.1') from gi.repository import AppIndicator3 as appindicator indicator = appindicator.Indicator.new(APPINDICATOR_ID, self.tray_icon_path, appindicator.IndicatorCategory.APPLICATION_STATUS) indicator.set_status(appindicator.IndicatorStatus.ACTIVE) indicator.set_menu(menu) # use .set_name to prevent error, Gdk-CRITICAL **: gdk_window_thaw_toplevel_updates: assertion 'window->update_and_descendants_freeze_count > 0' failed indicator.set_name = APPINDICATOR_ID # can set label beside systray icon # indicator.set_label('1.2 MB/s', '') self.active = True log('Systray active backend: Gtk.AppIndicator') Gtk.main() return except: pass # try GTK StatusIcon def icon_right_click(icon, button, time): menu.popup(None, None, None, icon, button, time) icon = Gtk.StatusIcon() icon.set_from_file(self.tray_icon_path) # DeprecationWarning: Gtk.StatusIcon.set_from_file is deprecated icon.connect("popup-menu", icon_right_click) # right click icon.connect('activate', self.show_main_window) # left click icon.set_name = APPINDICATOR_ID self.active = True log('Systray active backend: Gtk.StatusIcon') Gtk.main() return except Exception as e: log('Systray Gtk 3.0:', e, log_level=2) self.active = False else: # let pystray run for other platforms, basically windows try: from pystray import Icon, Menu, MenuItem items = [] for option, callback in options_map.items(): items.append(MenuItem(option, callback, default=True if option == 'Show' else False)) menu = Menu(*items) self.icon = Icon(config.APP_NAME, self.tray_icon, menu=menu) self.active = True self.icon.run() except Exception as e: log('systray: - run() - ', e) self.active = False def shutdown(self): try: self.active = False self.icon.stop() # must be called from main thread except: pass try: # if we use Gtk notify we should deinitialize self.Gtk_notify.uninit() except: pass try: # Gtk.main_quit(), if called from a thread might raise # (Gtk-CRITICAL **:gtk_main_quit: assertion 'main_loops != NULL' failed) # should call this from main thread self.Gtk.main_quit() except: pass def notify(self, msg, title=None): """show os notifications, e.g. balloon pop up at systray icon on windows""" if getattr(self.icon, 'HAS_NOTIFICATION', False): self.icon.notify(msg, title=title) else: try: self.Gtk_notify.Notification.new(title, msg, self.tray_icon_path).show() # to show notification except: try: # fallback to plyer import plyer plyer.notification.notify(message=msg, title=title, app_name=config.APP_NAME, app_icon=self.tray_icon_path, timeout=5) except: pass def quit(self, *args): """callback when selecting quit from systray menu""" # thread safe call for main window close self.main_window.run_method(self.main_window.quit)
class Updater: def __init__(self, options={}): self.__main_gui = None self.__config = config.AppConfig('dnfdragora') self.__updateInterval = 180 self.__update_count = -1 self.__log_enabled = False self.__log_directory = None self.__level_debug = False self.__hide_menu = True if self.__config.userPreferences: if 'settings' in self.__config.userPreferences.keys(): settings = self.__config.userPreferences['settings'] if 'interval for checking updates' in settings.keys(): self.__updateInterval = int( settings['interval for checking updates']) self.__hide_menu = settings['hide_update_menu'] if 'hide_update_menu' in settings.keys() \ else False #### Logging if 'log' in settings.keys(): log = settings['log'] if 'enabled' in log.keys(): self.__log_enabled = log['enabled'] if self.__log_enabled: if 'directory' in log.keys(): self.__log_directory = log['directory'] if 'level_debug' in log.keys(): self.__level_debug = log['level_debug'] if self.__log_enabled: if self.__log_directory: log_filename = os.path.join(self.__log_directory, "dnfdragora-updater.log") if self.__level_debug: misc.logger_setup(log_filename, loglvl=logging.DEBUG) else: misc.logger_setup(log_filename) print("Logging into %s, debug mode is %s" % (self.__log_directory, ("enabled" if self.__level_debug else "disabled"))) logger.info("dnfdragora-updater started") else: print("Logging disabled") # if missing gets the default icon from our folder (same as dnfdragora) icon_path = '/usr/share/dnfdragora/images/' if 'icon-path' in options.keys(): icon_path = options['icon-path'] if icon_path.endswith('/'): icon_path = icon_path + 'dnfdragora.svg' if (os.path.exists( icon_path + 'dnfdragora.svg')) else icon_path + 'dnfdragora.png' else: icon_path = icon_path + '/dnfdragora.svg' if (os.path.exists( icon_path + '/dnfdragora.svg')) else icon_path + '/dnfdragora.png' theme_icon_pathname = icon_path if 'icon-path' in options.keys( ) else self.__get_theme_icon_pathname() or icon_path logger.debug("Icon: %s" % (theme_icon_pathname)) #empty icon as last chance self.__icon = Image.Image() try: if theme_icon_pathname.endswith('.svg'): with open(theme_icon_pathname, 'rb') as svg: self.__icon = self.__svg_to_Image(svg.read()) else: self.__icon = Image.open(theme_icon_pathname) except Exception as e: logger.error(e) logger.error("Cannot open theme icon using default one %s" % (icon_path)) self.__icon = Image.open(icon_path) # resetting icon_path to default value icon_path = '/usr/share/dnfdragora/images/' if 'icon-path' in options.keys(): icon_path = options['icon-path'] if icon_path.endswith('/'): icon_path = icon_path + 'dnfdragora-update.svg' if ( os.path.exists(icon_path + 'dnfdragora-update.svg') ) else icon_path + 'dnfdragora-update.png' else: icon_path = icon_path + '/dnfdragora-update.svg' if ( os.path.exists(icon_path + '/dnfdragora-update.svg') ) else icon_path + '/dnfdragora-update.png' theme_icon_pathname = icon_path if 'icon-path' in options.keys( ) else self.__get_theme_icon_pathname( name="dnfdragora-update") or icon_path self.__icon_update = Image.Image() try: if theme_icon_pathname.endswith('.svg'): with open(theme_icon_pathname, 'rb') as svg: self.__icon_update = self.__svg_to_Image(svg.read()) else: self.__icon_update = Image.open(theme_icon_pathname) except Exception as e: logger.error(e) logger.error("Cannot open theme icon using default one %s" % (icon_path)) self.__icon_update = Image.open(icon_path) try: self.__backend = dnfd_client.Client() except dnfdaemon.client.DaemonError as error: logger.error( _('Error starting dnfdaemon service: [%s]') % (str(error))) return except Exception as e: logger.error( _('Error starting dnfdaemon service: [%s]') % (str(e))) return self.__running = True self.__updater = threading.Thread(target=self.__update_loop) self.__scheduler = sched.scheduler(time.time, time.sleep) self.__getUpdatesRequested = False self.__menu = Menu( MenuItem(_('Update'), self.__run_update), MenuItem(_('Open dnfdragora dialog'), self.__run_dnfdragora), MenuItem(_('Check for updates'), self.__check_updates), MenuItem(_('Exit'), self.__shutdown)) self.__name = 'dnfdragora-updater' self.__tray = Tray(self.__name, icon=self.__icon, title=self.__name, menu=self.__menu) def __get_theme_icon_pathname(self, name='dnfdragora'): ''' return theme icon pathname or None if missing ''' try: import xdg.IconTheme except ImportError: logger.error("Error: module xdg.IconTheme is missing") return None else: pathname = xdg.IconTheme.getIconPath(name, 256) return pathname return None def __svg_to_Image(self, svg_string): ''' gets svg content and returns a PIL.Image object ''' import cairosvg import io in_mem_file = io.BytesIO() cairosvg.svg2png(bytestring=svg_string, write_to=in_mem_file) return Image.open(io.BytesIO(in_mem_file.getvalue())) def __shutdown(self, *kwargs): logger.info("shutdown") if self.__main_gui: logger.warning("Cannot exit dnfdragora is not deleted %s" % ("and RUNNING" if self.__main_gui.running else "but NOT RUNNING")) return try: self.__running = False self.__updater.join() try: if self.__backend: self.__backend.Exit() self.__backend = None except: pass yui.YDialog.deleteAllDialogs() yui.YUILoader.deleteUI() except: pass finally: if not self.__scheduler.empty(): for task in self.__scheduler.queue: try: self.__scheduler.cancel(task) except: pass if self.__tray != None: self.__tray.stop() if self.__backend: self.__backend.Exit(False) time.sleep(0.5) self.__backend = None def __reschedule_update_in(self, minutes): ''' clean up scheduler and schedule update in 'minutes' ''' logger.debug("rescheduling") if not self.__scheduler.empty(): logger.debug("Reset scheduler") for task in self.__scheduler.queue: try: self.__scheduler.cancel(task) except: pass if self.__scheduler.empty(): self.__scheduler.enter(minutes * 60, 1, self.__get_updates) logger.info("Scheduled check for updates in %d %s", minutes if minutes >= 1 else minutes * 60, "minutes" if minutes >= 1 else "seconds") return True return False def __run_dialog(self, args, *kwargs): if self.__tray != None and self.__main_gui == None and self.__tray.visible: if self.__hide_menu: self.__tray.visible = False time.sleep(0.5) try: self.__main_gui = ui.mainGui(args) except Exception as e: logger.error( "Exception on running dnfdragora with args %s - %s", str(args), str(e)) dialogs.warningMsgBox({ 'title': _("Running dnfdragora failure"), "text": str(e), "richtext": True }) yui.YDialog.deleteAllDialogs() time.sleep(0.5) self.__main_gui = None return #self.__tray.icon = None self.__main_gui.handleevent() logger.debug("Closing dnfdragora") while self.__main_gui.loop_has_finished != True: time.sleep(1) logger.info("Closed dnfdragora") yui.YDialog.deleteAllDialogs() time.sleep(1) self.__main_gui = None logger.debug("Look for remaining updates") # Let's delay a bit the check, otherwise Lock will fail done = self.__reschedule_update_in(0.5) logger.debug("Scheduled %s", "done" if done else "skipped") else: if self.__main_gui: logger.warning( "Cannot run dnfdragora because it is already running") else: logger.warning("Cannot run dnfdragora") def __run_dnfdragora(self, *kwargs): logger.debug("Menu visibility is %s", str(self.__tray.visible)) return self.__run_dialog({}) def __run_update(self, *kwargs): logger.debug("Menu visibility is %s", str(self.__tray.visible)) return self.__run_dialog({'update_only': True}) def __check_updates(self, *kwargs): ''' Start get updates by simply locking the DB ''' logger.debug("Start checking for updates, by menu command") if self.__hide_menu: self.__tray.visible = False try: self.__backend.Lock() self.__getUpdatesRequested = True except Exception as e: logger.error(_('Exception caught: [%s]') % (str(e))) def __get_updates(self, *kwargs): ''' Start get updates by simply locking the DB ''' logger.debug("Start getting updates") try: self.__backend.Lock() except Exception as e: logger.error(_('Exception caught: [%s]') % (str(e))) def __OnRepoMetaDataProgress(self, name, frac): '''Repository Metadata Download progress.''' values = (name, frac) #print('on_RepoMetaDataProgress (root): %s', repr(values)) if frac == 0.0 or frac == 1.0: logger.debug('OnRepoMetaDataProgress: %s', repr(values)) def __update_loop(self): self.__get_updates() backend_locked = False while self.__running == True: update_next = self.__updateInterval add_to_schedule = False try: counter = 0 count_max = 1000 #if dnfdragora is running we receive transaction/rpm progress/download etc events #let's dequeue them as quick as possible while counter < count_max: counter = counter + 1 item = self.__backend.eventQueue.get_nowait() event = item['event'] info = item['value'] if (event == 'Lock'): logger.info("Event received %s - info %s", event, str(info)) backend_locked = info['result'] if backend_locked: self.__backend.GetPackages('updates_all') logger.debug("Getting update packages") else: # no locked try again in a minute update_next = 1 add_to_schedule = True elif (event == 'OnRepoMetaDataProgress'): #let's log metadata since slows down the Lock requests self.__OnRepoMetaDataProgress(info['name'], info['frac']) elif (event == 'GetPackages'): logger.debug( "Got GetPackages event menu visibility is %s", str(self.__tray.visible)) #if not self.__tray.visible : # ugly workaround to show icon if hidden, set empty icon and show it self.__tray.icon = Image.Image() self.__tray.visible = True logger.debug("Event received %s", event) if not info['error']: po_list = info['result'] self.__update_count = len(po_list) logger.info("Found %d updates" % (self.__update_count)) if (self.__update_count >= 1): self.__tray.icon = self.__icon_update self.__tray.visible = True self.__tray.notify( title='dnfdragora-update', message=_('%d updates available.') % self.__update_count) elif self.__getUpdatesRequested: # __update_count == 0 but get updates has been requested by user command # Let's give a feed back anyway logger.debug( "No updates found after user request") self.__tray.icon = self.__icon self.__tray.notify( title='dnfdragora-update', message=_('No updates available')) self.__tray.visible = not self.__hide_menu else: self.__tray.icon = self.__icon self.__tray.visible = not self.__hide_menu logger.debug("No updates found") self.__getUpdatesRequested = False logger.debug("Menu visibility is %s", str(self.__tray.visible)) else: # error logger.error("GetPackages error %s", str(info['error'])) #force scheduling again add_to_schedule = True # Let's release the db self.__backend.Unlock(sync=True) backend_locked = False logger.debug("RPM DB unlocked") #elif (event == xxx) else: if backend_locked: logger.warning( "Unmanaged event received %s - info %s", event, str(info)) except Empty as e: pass if add_to_schedule: self.__reschedule_update_in(update_next) elif self.__scheduler.empty(): # if the scheduler is empty we schedule a check according # to configuration file anyway self.__scheduler.enter(update_next * 60, 1, self.__get_updates) logger.info("Scheduled check for updates in %d minutes", update_next) self.__scheduler.run(blocking=False) time.sleep(0.5) logger.info("Update loop end") def __main_loop(self): def setup(tray): # False to start without icon tray.visible = True self.__updater.start() time.sleep(1) self.__tray.run(setup=setup) logger.info("dnfdragora-updater termination") def main(self): self.__main_loop()
class Updater: def __init__(self, options={}): self.__main_gui = None self.__notifier = notify2.Notification('dnfdragora', '', 'dnfdragora') self.__running = True self.__updater = threading.Thread(target=self.__update_loop) self.__scheduler = sched.scheduler(time.time, time.sleep) self.__config = config.AppConfig('dnfdragora') self.__updateInterval = 180 self.__update_count = -1 if self.__config.systemSettings : settings = {} if 'settings' in self.__config.systemSettings.keys() : settings = self.__config.systemSettings['settings'] if 'update_interval' in settings.keys() : self.__updateInterval = int(settings['update_interval']) if self.__config.userPreferences: if 'settings' in self.__config.userPreferences.keys() : settings = self.__config.userPreferences['settings'] if 'interval for checking updates' in settings.keys() : self.__updateInterval = int(settings['interval for checking updates']) # if missing gets the default icon from our folder (same as dnfdragora) icon_path = '/usr/share/dnfdragora/images/' if 'icon-path' in options.keys() : icon_path = options['icon-path'] if icon_path.endswith('/'): icon_path = icon_path + 'dnfdragora.png' else: icon_path = icon_path + '/dnfdragora.png' theme_icon_pathname = self.__get_theme_icon_pathname() or icon_path print("Icon: %s"%(theme_icon_pathname)) #empty icon as last chance self.__icon = Image.Image() try: if theme_icon_pathname.endswith('.svg'): with open(theme_icon_pathname, 'rb') as svg: self.__icon = self.__svg_to_Image(svg.read()) else: self.__icon = Image.open(theme_icon_pathname) except Exception as e: print(e) print("Cannot open theme icon using default one %s"%(icon_path)) self.__icon = Image.open(icon_path) self.__menu = Menu( MenuItem(_('Update'), self.__run_update), MenuItem(_('Open dnfdragora dialog'), self.__run_dnfdragora), MenuItem(_('Check for updates'), self.__get_updates_forced), MenuItem(_('Exit'), self.__shutdown) ) self.__name = 'dnfdragora-updater' self.__tray = Tray(self.__name, self.__icon, self.__name, self.__menu) def __get_theme_icon_pathname(self): ''' return theme icon pathname or None if missing ''' try: import xdg.IconTheme except ImportError: print ("Error: module xdg.IconTheme is missing") return None else: pathname = xdg.IconTheme.getIconPath("dnfdragora", 128) return pathname return None def __svg_to_Image(self, svg_string): ''' gets svg content and returns a PIL.Image object ''' import cairosvg import io in_mem_file = io.BytesIO() cairosvg.svg2png(bytestring=svg_string, write_to=in_mem_file) return Image.open(io.BytesIO(in_mem_file.getvalue())) def __shutdown(self, *kwargs): print("shutdown") if self.__main_gui : print("----> %s"%("RUN" if self.__main_gui.running else "NOT RUNNING")) return try: self.__running = False self.__updater.join() try: if self.__backend: self.__backend.Unlock() self.__backend.Exit() except: pass yui.YDialog.deleteAllDialogs() yui.YUILoader.deleteUI() except: pass finally: if self.__scheduler.empty() != False: for task in self.__scheduler.queue: try: self.__scheduler.cancel(task) except: pass if self.__tray != None: self.__tray.stop() if self.__backend: self.__backend.Exit() def __run_dialog(self, args, *kwargs): if self.__tray != None and self.__main_gui == None: time.sleep(0.5) try: self.__main_gui = ui.mainGui(args) except Exception as e: dialogs.warningMsgBox({'title' : _("Running dnfdragora failure"), "text": str(e), "richtext":True}) yui.YDialog.deleteAllDialogs() time.sleep(0.5) self.__main_gui = None return self.__tray.icon = None self.__main_gui.handleevent() while self.__main_gui.loop_has_finished != True: time.sleep(1) yui.YDialog.deleteAllDialogs() time.sleep(1) self.__main_gui = None self.__get_updates() def __run_dnfdragora(self, *kwargs): return self.__run_dialog({}) def __run_update(self, *kwargs): return self.__run_dialog({'update_only': True}) def __get_updates(self, *kwargs): return self.__get_updates_func(False) def __get_updates_forced(self, *kwargs): return self.__get_updates_func(True) def __get_updates_func(self, forced, *kwargs): try: self.__backend = dnfdaemon.client.Client() except dnfdaemon.client.DaemonError as error: print(_('Error starting dnfdaemon service: [%s]')%(str(error))) self.__update_count = -1 self.__tray.icon = None return except Exception as e: print(_('Error starting dnfdaemon service: [%s]')%( str(e))) self.__update_count = -1 self.__tray.icon = None return try: if self.__backend.Lock(): pkgs = self.__backend.GetPackages('updates') self.__update_count = len(pkgs) self.__backend.Unlock() self.__backend.Exit() time.sleep(0.5) self.__backend = None if (self.__update_count >= 1) or forced: self.__notifier.update( 'dnfdragora', _('%d updates available.') % self.__update_count, 'dnfdragora' ) self.__notifier.show() self.__tray.icon = self.__icon self.__tray.visible = True else: self.__notifier.close() else: print("DNF backend already locked cannot check for updates") self.__update_count = -1 self.__tray.icon = None self.__backend = None except Exception as e: print(_('Exception caught: [%s]')%(str(e))) def __update_loop(self): self.__get_updates() while self.__running == True: if self.__scheduler.empty(): self.__scheduler.enter(self.__updateInterval * 60, 1, self.__get_updates) self.__scheduler.run(blocking=False) time.sleep(1) def __main_loop(self): def setup(tray) : tray.visible = False self.__updater.start() time.sleep(1) self.__tray.run(setup=setup) print("dnfdragora-updater termination") def main(self): self.__main_loop()
def run_tray_icon(exit_callback): menu = Menu(MenuItem("Exit", exit_callback)) icon_image = get_icon_image() icon = Icon(TRAY_ICON_TITLE, icon_image, menu=menu) icon.run()