Exemplo n.º 1
1
    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))
Exemplo n.º 2
0
    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))
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
    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()
Exemplo n.º 6
0
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()
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
    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()
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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")
Exemplo n.º 11
0
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()
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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()
Exemplo n.º 15
0
    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()
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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()
Exemplo n.º 18
0
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
Exemplo n.º 19
0
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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'
Exemplo n.º 20
0
                    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
Exemplo n.º 21
0
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)
Exemplo n.º 22
0
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)
Exemplo n.º 24
0
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()
Exemplo n.º 25
0
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())
Exemplo n.º 26
0
        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()
Exemplo n.º 27
0
    # 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)
Exemplo n.º 28
0
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)
Exemplo n.º 29
0
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)
Exemplo n.º 30
0
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()
Exemplo n.º 31
0
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()