Пример #1
0
class sys_tray():
    def __init__(self, _fnc):
        self.fnc = _fnc

    def pas_thq(self, sysTrayIcon):
        global var_thq
        var_thq = "exit"

    def stray_dst(self, sysTrayIcon):
        self.systrayicon.shutdown()

    # def setng(self, sysTrayIcon):
    #     pass

    def stray_strt(self, sysTrayIcon):
        menu_options = ()  # (('Settings', None, self.setng),)
        self.systrayicon = SysTrayIcon("va.ico",
                                       "Virtual Assistant",
                                       menu_options,
                                       on_quit=self.pas_thq)
        self.systrayicon.start()
Пример #2
0
class Trayer():
    def __init__(self, mouse_tracker):
        self.mouse_tracker = mouse_tracker
        self.activated = True
        self.menu_options = (
            ("Activate", None, self.activate),
            ("Deactivate", None, self.deactivate),
        )
        self.systray = SysTrayIcon("bogazici.ico", "BUICS Security Lab",
                                   self.menu_options)
        self.mouse_tracker.start_listening()

    def start(self):
        self.systray.start()

    def activate(self, systray):
        self.activated = True
        self.mouse_tracker.start_listening()
        print("Activated")

    def deactivate(self, systray):
        self.activated = False
        self.mouse_tracker.stop_listening()
        print("Deactivated")
Пример #3
0
class SnuClipboardManager(NormalApp):
    icon = 'data/icon_small.png'
    tray = None
    tray_thread = None
    window_minimized = BooleanProperty(False)
    shift_pressed = BooleanProperty(False)
    ctrl_pressed = BooleanProperty(False)
    alt_pressed = BooleanProperty(False)
    window_leave_timeout = ObjectProperty(allownone=True)
    on_top = BooleanProperty(False)
    window_min_width = NumericProperty(130)
    window_title = 'Snu Clipboard Manager'
    window_width_target = NumericProperty(200)
    modify_mode = BooleanProperty(False)
    clipboard_updater = ObjectProperty(allownone=True)
    current_clipboard = StringProperty('')
    clipboard_folder = StringProperty('')
    clipboard_folders = ListProperty()
    clipboard_data = ListProperty()
    clipboard_data_display = ListProperty()
    clipboard_history = ListProperty()
    hiddens = ListProperty()
    main_history_length = NumericProperty(1)
    data_directory = StringProperty('')
    show_undo = BooleanProperty(True)
    show_strip = BooleanProperty(False)
    max_history = NumericProperty(20)

    preset_type = StringProperty()
    preset_element = StringProperty()
    preset_second_element = StringProperty()

    edit_type = StringProperty()
    edit_path = StringProperty()
    edit_name = StringProperty()
    edit_name_original = StringProperty()
    edit_section = StringProperty()
    edit_content = StringProperty()

    search_text = StringProperty()
    search_results = ListProperty()
    search_content = BooleanProperty(True)

    def search(self, *_):
        data = []
        if self.search_text:
            search_text = self.search_text.lower()
            for clip in self.clipboard_data:
                if clip['viewtype'] == 'item':
                    if (search_text in clip['text'].lower()) or (
                            self.search_content
                            and search_text in clip['clipboard'].lower()):
                        new_clip = clip.copy()
                        new_clip['hidden'] = False
                        new_clip['show_folder'] = True
                        data.append(new_clip)
        self.search_results = data

    def dismiss_popup(self, *_):
        if self.popup is not None:
            try:
                self.popup.dismiss()
            except:
                pass
            self.popup = None

    def rescale_interface(self, *_, force=False):
        """Called when the window changes resolution, calculates variables dependent on screen size"""

        self.store_window_size()

        if Window.width != self.last_width:
            self.last_width = Window.width
            self.popup_x = min(Window.width, 640)

        if (Window.height != self.last_height) or force:
            self.last_height = Window.height
            self.button_scale = int(
                float(cm(0.85)) *
                (int(self.config.get("Settings", "buttonsize")) / 100))
            self.text_scale = int(
                (self.button_scale / 3) *
                int(self.config.get("Settings", "textsize")) / 100)
            self.display_border = self.button_scale / 3
            self.display_padding = self.button_scale / 4

    def get_application_config(self, **kwargs):
        if platform == 'win':
            self.data_directory = os.getenv(
                'APPDATA') + os.path.sep + "Snu Clipboard Manager"
            if not os.path.isdir(self.data_directory):
                os.makedirs(self.data_directory)
        elif platform == 'linux':
            self.data_directory = os.path.expanduser(
                '~') + os.path.sep + ".snuclipboardmanager"
            if not os.path.isdir(self.data_directory):
                os.makedirs(self.data_directory)
        elif platform == 'macosx':
            self.data_directory = os.path.expanduser(
                '~') + os.path.sep + ".snuclipboardmanager"
            if not os.path.isdir(self.data_directory):
                os.makedirs(self.data_directory)
        elif platform == 'android':
            self.data_directory = self.user_data_dir
        else:
            self.data_directory = os.path.sep
        config_file = os.path.realpath(
            os.path.join(self.data_directory, "snuclipboardmanager.ini"))
        return config_file

    def on_config_change(self, config, section, key, value):
        self.load_config_values()
        self.rescale_interface(force=True)

    def load_config_values(self):
        self.show_undo = self.config.getboolean("Settings", 'showundo')
        self.show_strip = self.config.getboolean("Settings", 'showstrip')
        max_history = int(self.config.get("Settings", 'max_history'))
        if max_history < 1:
            self.max_history = 1
        else:
            self.max_history = max_history
        main_history_length = int(
            self.config.get("Settings", "main_history_length"))
        if main_history_length > self.max_history:
            self.main_history_length = self.max_history
        elif main_history_length < 1:
            self.main_history_length = 1
        else:
            self.main_history_length = main_history_length

    def load_clipboards(self, edit=False):
        self.load_hiddens()
        self.clipboard_data = []
        if edit:
            self.clipboard_data.append({
                'text': 'Add Section',
                'viewtype': 'adder-heading',
                'hidden': False,
                'section': self.clipboard_folder,
                'path': self.clipboard_folder,
                'show_folder': False
            })
        if os.path.isdir(self.clipboard_folder):
            sections = os.listdir(self.clipboard_folder)
            for section in sections:
                if section in self.hiddens and not edit:
                    hidden = True
                else:
                    hidden = False
                section_folder = os.path.join(self.clipboard_folder, section)
                if os.path.isdir(section_folder):
                    self.clipboard_data.append({
                        'text': section,
                        'viewtype': 'heading',
                        'hidden': hidden,
                        'section': section,
                        'path': section_folder,
                        'section_folder': self.clipboard_folder,
                        'show_folder': False
                    })
                    if edit:
                        self.clipboard_data.append({
                            'text': 'Add Item',
                            'viewtype': 'adder-item',
                            'hidden': hidden,
                            'section': section,
                            'path': section_folder,
                            'section_folder': self.clipboard_folder,
                            'show_folder': False
                        })
                    for file in os.listdir(section_folder):
                        fullpath = os.path.join(section_folder, file)
                        if os.path.isfile(fullpath):
                            with open(fullpath, 'r') as datafile:
                                try:
                                    data = datafile.read()
                                except:
                                    continue
                            self.clipboard_data.append({
                                'text':
                                os.path.splitext(file)[0],
                                'viewtype':
                                'item',
                                'clipboard':
                                data,
                                'hidden':
                                hidden,
                                'section':
                                section,
                                'path':
                                fullpath,
                                'section_folder':
                                section_folder,
                                'show_folder':
                                False
                            })
        self.update_display_clipboards()

    def toggle_hide_section(self, section, hide_others=False):
        for data in self.clipboard_data:
            if data['section'] == section:
                if hide_others:
                    data['hidden'] = False
                else:
                    data['hidden'] = not data['hidden']
            elif hide_others:
                data['hidden'] = True
        self.save_hiddens()
        self.root.ids.presets.refresh_from_data()
        self.update_display_clipboards()

    def update_display_clipboards(self, *_):
        data = []
        for clip in self.clipboard_data:
            if not clip['hidden'] or clip['viewtype'] == 'heading':
                data.append(clip)
        self.clipboard_data_display = data

    def clear_history(self):
        self.clipboard_history = self.clipboard_history[:1]

    def remove_history_item(self, index):
        if index > 0:
            self.clipboard_history.pop(index)

    def remove_history_matches(self, clipboard):
        for clip in reversed(self.clipboard_history):
            if clip['text'] == clipboard:
                self.clipboard_history.remove(clip)

    def set_history_item(self, index):
        try:
            history_item = self.clipboard_history.pop(index)
        except:
            return
        self.clipboard_history.insert(0, history_item)
        self.set_clipboard(history_item['text'], skip_history=True)

    def set_clipboard_from_widget(self, widget):
        if widget.focus:
            self.set_clipboard(widget.text, overwrite_history=True)

    def set_clipboard(self, text, skip_history=False, overwrite_history=False):
        #Sets a preset to the clipboard
        if text and text != self.current_clipboard:
            Clipboard.copy(text)
            if overwrite_history:
                if len(self.clipboard_history) == 0:
                    self.clipboard_history.append({'text': text})
                else:
                    self.clipboard_history[0]['text'] = text
                self.current_clipboard = text
                self.refresh_history_areas()
            if skip_history:
                self.current_clipboard = text

    def refresh_history_areas(self):
        self.root.ids.historyFull.refresh_from_data()
        self.root.ids.historyShort.refresh_from_data()

    def update_current_clipboard(self, *_):
        #Sets the current clipboard to a local variable

        clipboard = Clipboard.paste()
        if clipboard and self.current_clipboard != clipboard:
            self.current_clipboard = clipboard
            if self.config.getboolean('Settings', 'no_duplicates'):
                self.remove_history_matches(clipboard)
            self.clipboard_history.insert(0, {'text': clipboard})
            self.clipboard_history = self.clipboard_history[:self.max_history]

    def undo_clipboard(self, *_):
        self.set_history_item(1)

    def strip_clipboard(self, *_):
        Clipboard.copy(self.current_clipboard.strip())

    def settings_mode(self, heading=''):
        self.close_settings()
        if self.modify_mode:
            self.modify_mode = False
            if self.root.ids.editArea.current == 'edit':
                self.load_clipboards()
        else:
            self.modify_mode = True
            if self.root.ids.editArea.current == 'edit':
                self.load_clipboards(edit=True)
        self.size_window()

    def delete_preset(self, preset):
        self.preset_type = preset.viewtype
        self.preset_element = preset.path

        if preset.viewtype == 'heading':
            confirm_text = "This entire section of presets will be deleted permanently"
        else:
            confirm_text = "This single preset will be deleted permanently"
        self.dismiss_popup()
        content = ConfirmPopupContent(text=confirm_text,
                                      yes_text='Delete',
                                      no_text="Don't Delete",
                                      warn_yes=True)
        content.bind(on_answer=self.delete_preset_answer)
        self.popup = NormalPopup(title="Confirm Delete ",
                                 content=content,
                                 size_hint=(1, None),
                                 size=(1000, self.button_scale * 4))
        self.popup.bind(on_dismiss=self.dismiss_popup)
        self.popup.open()

    def delete_preset_answer(self, instance, answer):
        self.dismiss_popup()
        if answer == 'yes':
            if self.preset_type == 'item':
                if os.path.isfile(self.preset_element):
                    os.remove(self.preset_element)
                    self.load_clipboards(edit=True)
            elif self.preset_type == 'heading':
                if os.path.isdir(self.preset_element):
                    shutil.rmtree(self.preset_element)
                    self.load_clipboards(edit=True)

    def edit_preset(self, preset):
        if preset.viewtype == 'item':
            self.edit_type = preset.viewtype
            self.edit_path = preset.path
            self.edit_name = preset.text
            self.edit_name_original = preset.text
            self.edit_section = preset.section_folder
            self.edit_content = preset.clipboard
            self.root.ids.editContentArea.focus = True
        elif preset.viewtype == 'heading':
            self.preset_type = 'heading'
            self.preset_element = preset.section_folder
            self.preset_second_element = preset.text
            content = InputPopupContent(text="Rename folder to:",
                                        input_text=preset.text,
                                        hint='Folder Name')
            content.bind(on_answer=self.rename_folder_answer)
            self.popup = NormalPopup(title="Rename Folder",
                                     content=content,
                                     size_hint=(1, None),
                                     size=(1000, self.button_scale * 5))
            self.popup.bind(on_dismiss=self.dismiss_popup)
            self.popup.open()

    def rename_folder_answer(self, instance, answer):
        new_name = instance.ids["input"].text.strip(' ')
        self.dismiss_popup()
        if not new_name:
            return
        if answer == 'yes':
            old_path = os.path.join(self.preset_element,
                                    self.preset_second_element)
            new_path = os.path.join(self.preset_element, new_name)
            if old_path != new_path:
                try:
                    os.rename(old_path, new_path)
                    if self.edit_section == old_path:
                        self.edit_path = os.path.join(
                            new_path, self.edit_name_original) + '.txt'
                        self.edit_section = new_path
                    self.load_clipboards(edit=True)
                except Exception as e:
                    app.message_popup(text="Unable to rename folder: " +
                                      str(e),
                                      title="Warning")

    def save_edit(self):
        if not self.edit_path or not self.edit_name or not self.edit_section:
            return
        new_file = os.path.join(self.edit_section, self.edit_name) + '.txt'
        if new_file != self.edit_path:
            #rename file
            try:
                os.rename(self.edit_path, new_file)
                self.edit_path = new_file
            except Exception as e:
                app.message_popup(text="Unable to save edit: " + str(e),
                                  title="Warning")
                return
        try:
            file = open(self.edit_path, "w")
            file.write(self.edit_content)
            file.close()
            self.load_clipboards(edit=True)
        except Exception as e:
            app.message_popup(text="Unable to write to file: " + str(e),
                              title="Warning")

    def clear_edit(self, *_):
        self.edit_content = ''
        self.edit_name = ''
        self.edit_name_original = ''
        self.edit_path = ''
        self.edit_section = ''

    def scroll_presets_to(self, section, item):
        scroll_to_index = None
        preset_area = self.root.ids['presets']
        for index, data in enumerate(preset_area.data):
            if not item:
                if data['viewtype'] == 'heading' and data['section'] == section:
                    scroll_to_index = index + 2
                    break
            else:
                if data['section'] == section and data[
                        'viewtype'] == 'item' and data['path'] == item:
                    scroll_to_index = index
                    break
        if scroll_to_index is not None:
            preset_area.scroll_to_index(scroll_to_index)

    def instant_add(self, path, section):
        self.dismiss_popup()
        if not self.modify_mode:
            self.settings_mode()
        self.root.ids.editArea.current = 'edit'
        self.load_clipboards(edit=True)
        self.edit_type = 'item'
        self.edit_path = path
        self.edit_name = ''
        self.edit_name_original = ''
        self.edit_section = section
        self.edit_content = self.current_clipboard
        self.scroll_presets_to(section, '')
        content = InstantAddPresetContent()
        content.bind(on_answer=self.instant_add_preset_answer)
        self.popup = NormalPopup(title='Create File',
                                 content=content,
                                 size_hint=(1, 1),
                                 size=(1000, 2000))
        self.popup.bind(on_dismiss=self.dismiss_popup)
        self.popup.bind(on_dismiss=self.clear_edit)
        self.popup.open()

    def instant_add_preset_answer(self, instance, answer):
        self.dismiss_popup()
        if answer == 'yes':
            filename = os.path.join(self.edit_path, self.edit_name) + '.txt'
            if os.path.exists(filename):
                self.message_popup(text="File already exists!",
                                   title="Warning")
                self.clear_edit()
                return
            file = open(filename, 'w')
            file.write(self.edit_content)
            file.close()
            self.load_clipboards()
        self.clear_edit()

    def add_preset(self, preset_type, preset_location):
        self.preset_type = preset_type
        self.preset_element = preset_location
        if preset_type == 'heading':
            input_text = "Add a folder with the name:"
            hint_text = "Folder Name"
            title_text = "Create Folder"
        else:
            input_text = "Add a preset with the name:"
            hint_text = "Preset Name"
            title_text = "Create File"
        self.dismiss_popup()

        content = InputPopupContent(text=input_text, hint=hint_text)
        content.bind(on_answer=self.add_preset_answer)
        self.popup = NormalPopup(title=title_text,
                                 content=content,
                                 size_hint=(1, None),
                                 size=(1000, self.button_scale * 5))
        self.popup.bind(on_dismiss=self.dismiss_popup)
        self.popup.open()

    def add_preset_answer(self, instance, answer):
        preset_name = instance.ids['input'].text.strip(' ')
        self.dismiss_popup()
        if not preset_name:
            return
        if answer == 'yes':
            preset_type = self.preset_type
            preset_location = self.preset_element
            if preset_type == 'heading':
                path = os.path.join(preset_location, preset_name)
                try:
                    os.makedirs(path)
                except Exception as e:
                    app.message_popup(text="Unable to create folder: " +
                                      str(e),
                                      title="Warning")
                self.load_clipboards(edit=True)
            else:
                filename = os.path.join(preset_location, preset_name) + '.txt'
                if not os.path.isfile(filename):
                    try:
                        file = open(filename, 'w+')
                        file.close()
                        self.edit_type = preset_type
                        self.edit_path = filename
                        self.edit_name = preset_name
                        self.edit_name_original = preset_name
                        self.edit_section = preset_location
                        self.edit_content = ''
                        self.root.ids.editContentArea.focus = True
                    except Exception as e:
                        app.message_popup(text="Unable to create file: " +
                                          str(e),
                                          title="Warning")
                    self.load_clipboards(edit=True)
                else:
                    app.message_popup(text="File already exists!",
                                      title="Warning")

    def load_clipboard_folders(self):
        folders = [self.clipboard_folder]
        folder_items = self.config.items("PresetsPaths")
        for key, folder in folder_items:
            if folder not in folders:
                folders.append(folder)
        folders_data = []
        for folder in folders:
            if folder:
                folders_data.append({'path': folder})
        self.clipboard_folders = folders_data

    def save_clipboard_folders(self):
        self.config.remove_section("PresetsPaths")
        self.config.add_section("PresetsPaths")
        for index, folder_item in enumerate(self.clipboard_folders):
            folder = folder_item['path']
            self.config.set("PresetsPaths", str(index), folder)

    def remove_presets_folder(self, path):
        for index, preset in enumerate(self.clipboard_folders):
            if preset['path'] == path:
                self.clipboard_folders.pop(index)
                self.save_clipboard_folders()

    def set_presets_folder(self, path):
        self.save_hiddens()
        self.clipboard_folder = path
        self.config.set("Settings", "presetsfolder", path)
        self.load_clipboards()

    def add_presets_folder(self, *_):
        self.not_on_top()
        from plyer import filechooser
        try:
            filechooser.choose_dir(on_selection=self.add_presets_folder_finish,
                                   title='Select A Presets Directory')
        except:
            #Filechooser can fail if user selects root
            if self.config.getboolean("Settings", "alwaysontop"):
                self.always_on_top()
            pass

    def add_presets_folder_finish(self, path):
        if self.config.getboolean("Settings", "alwaysontop"):
            self.always_on_top()
        path = path[0]
        if path:
            if path not in self.clipboard_folders:
                self.clipboard_folders.append({'path': path})
                self.save_clipboard_folders()
                self.set_presets_folder(path)
            else:
                self.message("Path Already Added")
        else:
            self.message("No Path Found")

    def browse_presets(self):
        try:
            import webbrowser
            webbrowser.open(self.clipboard_folder)
        except:
            pass

    def size_window(self, *_):
        Window.left = self.window_left
        Window.top = self.window_top
        height = self.window_height
        width = int(self.button_scale * 4 *
                    (float(self.config.get("Settings", "normalwidth")) / 100))
        if width < self.window_min_width:
            width = self.window_min_width
        if self.modify_mode:
            self.window_width_target = width
            width = int(
                self.button_scale * 17 *
                (float(self.config.get("Settings", "expandedwidth")) / 100))
            Window.size = width, height
        else:
            Window.size = width, height
            self.window_width_target = Window.width

    def on_button_scale(self, *_):
        self.size_window()

    def save_hiddens(self):
        if not self.clipboard_folder:
            return
        hiddens = []
        for data in self.clipboard_data:
            if data['hidden'] and data['viewtype'] == 'heading':
                section = data['section']
                if section not in hiddens:
                    hiddens.append(section)

        hiddens_filename = os.path.join(self.clipboard_folder, 'hiddens.txt')
        try:
            hiddens_file = open(hiddens_filename, 'w')
        except:
            return
        for hidden in hiddens:
            hiddens_file.write(hidden + '\n')
        hiddens_file.close()

    def load_hiddens(self):
        hiddens = []
        hiddens_filename = os.path.join(self.clipboard_folder, 'hiddens.txt')
        try:
            hiddens_file = open(hiddens_filename, 'r')
        except:
            self.hiddens = []
            return
        lines = hiddens_file.readlines()
        hiddens_file.close()
        for line in lines:
            line = line.strip('\n')
            if line:
                hiddens.append(line)
        self.hiddens = hiddens

    def build_config(self, config):
        """Setup config file if it is not found"""

        window_height, window_width, window_top, window_left = self.get_window_size(
        )
        config.setdefaults(
            'Settings', {
                'buttonsize': 100,
                'textsize': 100,
                'expandedwidth': 100,
                'normalwidth': 100,
                'alwaysontop': 1,
                'minimizestart': 0,
                'minimizetray': 0,
                'showtray': 1,
                'auto_shrink': 0,
                'auto_expand': 0,
                'no_duplicates': 0,
                'showundo': 1,
                'showstrip': 0,
                'presetsfolder': '',
                'max_history': 20,
                'main_history_length': 1,
                'window_height': window_height,
                'window_top': window_top,
                'window_left': window_left,
                'window_width': window_width
            })
        config.setdefaults('PresetsPaths', {})

    def build_settings(self, settings):
        """Kivy settings dialog panel
        settings types: title, bool, numeric, options, string, path"""

        settingspanel = []
        settingspanel.append({
            "type": "label",
            "title": "        Basic Behavior"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "Max Clipboard History",
            "desc":
            "Number of entries to keep in clipboard history. Defaluts to 20.",
            "section": "Settings",
            "key": "max_history"
        })
        settingspanel.append({
            "type": "bool",
            "title": "Auto-Shrink Window",
            "desc": "Automatically shrink the window when the mouse leaves.",
            "section": "Settings",
            "key": "auto_shrink"
        })
        settingspanel.append({
            "type": "bool",
            "title": "Auto-Expand Window",
            "desc": "Automatically expand the window when the mouse enters.",
            "section": "Settings",
            "key": "auto_expand"
        })
        settingspanel.append({
            "type": "bool",
            "title": "Minimize On Startup",
            "desc": "Automatically minimize the program as soon as it starts.",
            "section": "Settings",
            "key": "minimizestart"
        })
        if platform == 'win':
            settingspanel.append({
                "type": "bool",
                "title": "Always-On-Top",
                "desc":
                "Sets the window to always-on-top mode on startup.  Changing requires a program restart.",
                "section": "Settings",
                "key": "alwaysontop"
            })
            settingspanel.append({
                "type": "bool",
                "title": "Show Tray Icon",
                "desc":
                "A tray icon will be shown with options.  Changing requires program restart.",
                "section": "Settings",
                "key": "showtray"
            })
            settingspanel.append({
                "type": "bool",
                "title": "Minimize To Tray",
                "desc":
                "Minimize to tray when minimizing the window.  Only available when tray icon is enabled.",
                "section": "Settings",
                "key": "minimizetray"
            })
        settingspanel.append({
            "type": "bool",
            "title": "Disallow Duplicates",
            "desc":
            "Prevent duplicates from being added to clipboard history, removes the previous item when a new duplicate is added.",
            "section": "Settings",
            "key": "no_duplicates"
        })

        settingspanel.append({
            "type": "label",
            "title": "        Main Window Appearance"
        })
        settingspanel.append({
            "type": "bool",
            "title": "Show Undo Clipboard Button",
            "desc":
            "Shows a button to undo the clipboard to the previous state.",
            "section": "Settings",
            "key": "showundo"
        })
        settingspanel.append({
            "type": "bool",
            "title": "Show Strip Clipboard Button",
            "desc":
            "Shows a button to remove all formatting and extra spaces from clipboard text.",
            "section": "Settings",
            "key": "showstrip"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "History In Main Window",
            "desc":
            "Number of history elements to show in main window. Defaults to 1.",
            "section": "Settings",
            "key": "main_history_length"
        })

        settingspanel.append({
            "type": "label",
            "title": "        Interface Scaling"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "Shrunk Width Scale",
            "desc":
            "Scale of the window when it is in normal mode. Expects a percentage, defaults to 100",
            "section": "Settings",
            "key": "normalwidth"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "Expanded Width Scale",
            "desc":
            "Scale of the window when it is in expanded mode. Expects a percentage, defaults to 100",
            "section": "Settings",
            "key": "expandedwidth"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "Button Scale",
            "desc": "Button scale, expects a percentage, defaults to 100.",
            "section": "Settings",
            "key": "buttonsize"
        })
        settingspanel.append({
            "type": "numeric",
            "title": "Text Scale",
            "desc": "Font scale, expects a percentage, defaults to 100.",
            "section": "Settings",
            "key": "textsize"
        })
        settings.add_json_panel('Settings',
                                self.config,
                                data=json.dumps(settingspanel))

    def build(self):
        global app
        app = self
        if platform == 'win' and self.config.getboolean(
                "Settings", "showtray"):
            #setup system tray
            from infi.systray import SysTrayIcon
            tray_menu = (
                ("Toggle Minimize", None, self.tray_click),
                ("Undo Clipboard", None, self.undo_clipboard),
                ("Strip Clipboard", None, self.strip_clipboard),
            )
            icon_file = kivy.resources.resource_find('icon.ico')
            self.tray = SysTrayIcon(icon_file,
                                    'Snu Clipboard Manager',
                                    tray_menu,
                                    on_quit=self.tray_quit)
            self.tray.start()
        else:
            self.tray = None

        self.clipboard_folder = self.config.get("Settings", "presetsfolder")
        self.load_clipboard_folders()
        self.load_clipboards()
        Window.bind(on_key_down=self.key_down)
        Window.bind(on_key_up=self.key_up)
        Window.bind(on_cursor_leave=self.window_exited)
        Window.bind(on_cursor_enter=self.window_entered)
        Window.bind(on_minimize=self.on_minimize)
        Window.bind(on_restore=self.on_restore)
        return MainScreen()

    def tray_quit(self, tray):
        Clock.schedule_once(self.stop)

    def tray_click(self, tray):
        if self.window_minimized:
            Clock.schedule_once(self.restore)
        else:
            Clock.schedule_once(self.minimize)

    def restore(self, *_):
        Window.restore()

    def minimize(self, *_):
        Window.minimize()

    def on_restore(self, window):
        self.window_minimized = False
        Window.show()

    def on_minimize(self, window):
        self.window_minimized = True
        if self.config.getboolean("Settings",
                                  "minimizetray") and self.tray is not None:
            Window.hide()

    def window_exited(self, window):
        self.window_leave_timeout = Clock.schedule_once(
            self.window_exited_finish, 1)

    def window_entered(self, window):
        self.window_entered_finish()
        if self.window_leave_timeout is not None:
            self.window_leave_timeout.cancel()
            self.window_leave_timeout = None

    def window_exited_finish(self, *_):
        if self.config.getboolean("Settings", 'auto_shrink'):
            if self.modify_mode and self.popup is None and self.root.ids.editArea.current != 'edit':
                self.settings_mode()

    def window_entered_finish(self, *_):
        if self.config.getboolean("Settings", 'auto_expand'):
            if not self.modify_mode:
                self.settings_mode()

    def on_start(self):
        EventLoop.window.bind(on_keyboard=self.hook_keyboard)
        self.load_config_values()
        Window.set_title(self.window_title)
        if self.config.getboolean("Settings", "alwaysontop"):
            self.always_on_top()
        self.load_window_size()
        self.clipboard_updater = Clock.schedule_interval(
            self.update_current_clipboard, 0.1)
        if not self.clipboard_folder:
            self.root.ids.editArea.current = 'select'
            self.modify_mode = True
            Clock.schedule_once(self.add_presets_folder)
        else:
            self.modify_mode = False
        self.size_window()
        if self.config.getboolean("Settings", "minimizestart"):
            self.minimize()

    def always_on_top(self, *_):
        #Set on top mode
        self.on_top = True
        from KivyOnTop import register_topmost
        register_topmost(Window, self.window_title)

    def not_on_top(self, *_):
        self.on_top = False
        from KivyOnTop import unregister_topmost
        unregister_topmost(Window, self.window_title)

    def toggle_on_top(self):
        if self.on_top:
            self.not_on_top()
        else:
            self.always_on_top()

    def get_window_size(self):
        #Get window size
        if platform == 'win':
            SPI_GETWORKAREA = 48
            SM_CXSCREEN = 0
            SM_CYSCREEN = 1  #The height of the screen of the primary display monitor, in pixels.
            SM_CYCAPTION = 4  #The height of a caption area, in pixels.
            SM_CYBORDER = 6  #The height of a window border, in pixels.
            SM_CYFIXEDFRAME = 8  #The thickness of the frame around the perimeter of a window that has a caption but is not sizable, in pixels.

            from win32api import GetSystemMetrics
            taskbar_height = GetSystemMetrics(SPI_GETWORKAREA)
            screen_height = GetSystemMetrics(SM_CYSCREEN)
            screen_width = GetSystemMetrics(SM_CXSCREEN)
            window_frame_size = GetSystemMetrics(
                SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYBORDER)
            window_left_offset = window_frame_size
            window_top_offset = GetSystemMetrics(
                SM_CYCAPTION) + window_frame_size
            window_height = screen_height - window_top_offset - window_left_offset - taskbar_height
            window_width = screen_width - window_left_offset - window_left_offset
            return window_height, window_width, window_top_offset, window_left_offset
        else:
            return 680, self.window_min_width, 30, 5

    def on_pause(self):
        self.save_hiddens()
        self.config.write()
        return True

    def on_stop(self):
        if not (self.modify_mode and self.root.ids.editArea.current == 'edit'):
            self.save_hiddens()
        self.config.write()
        if self.tray is not None:
            self.tray.shutdown()

    def toggle_area(self, area):
        self.close_settings()
        if not self.modify_mode:
            self.root.ids.editArea.current = area
            self.modify_mode = True
            if area == 'edit':
                self.load_clipboards(edit=True)
            self.size_window()
        else:
            if self.root.ids.editArea.current == area:
                self.modify_mode = False
                self.load_clipboards()
                self.size_window()
            else:
                self.root.ids.editArea.current = area
                if area == 'edit':
                    self.load_clipboards(edit=True)

    def text_input_active(self):
        """Checks if any 'NormalInput' or 'FloatInput' widgets are currently active (being typed in).
        Returns: True or False
        """

        input_active = False
        for widget in self.root.walk(restrict=True):
            if widget.__class__.__name__ == 'NormalInput' or widget.__class__.__name__ == 'FloatInput' or widget.__class__.__name__ == 'IntegerInput':
                if widget.focus:
                    input_active = True
                    break
        return input_active

    def check_for_active_input(self, root):
        #Walks widget tree and checks for an active multiline text input, returns that input if found, otherwise returns None

        has_input = None
        for widget in root.walk(restrict=True):
            if isinstance(widget, TextInput):
                if widget.focus:
                    return widget
        return has_input

    def hook_keyboard(self, window, scancode, *_):
        """This function receives keyboard events"""

        #print(scancode)
        if scancode == 282:
            #f1 key
            if not self.modify_mode:
                self.settings_mode()
        if scancode == 27:
            #escape key
            if self.popup:
                self.popup.content.dispatch('on_answer', 'no')
                return True
        if scancode == 13:
            #enter key
            if self.popup:
                has_input = self.check_for_active_input(self.popup)
                if has_input is not None:
                    self.popup.content.dispatch('on_answer', 'yes')
        if scancode == 96:
            #tilde key
            if self.alt_pressed or self.ctrl_pressed or not self.text_input_active(
            ):
                self.settings_mode()
        if scancode == 283:
            #f2 key
            self.toggle_area('history')
        if scancode == 284:
            #f3 key
            self.toggle_area('edit')
        if scancode == 285:
            #f4 key
            self.toggle_area('select')
        if scancode == 97:
            #a key
            if self.alt_pressed:
                self.toggle_on_top()
        if scancode == 109:
            #m key
            if self.alt_pressed:
                self.minimize()

    def key_down(self, key, scancode=None, *_):
        """Intercepts various key presses and sends commands to the current screen."""

        del key
        if scancode == 307 or scancode == 308:
            #alt keys
            self.alt_pressed = True
        if scancode == 305 or scancode == 306:
            #ctrl keys
            self.ctrl_pressed = True
        if scancode == 303 or scancode == 304:
            #shift keys
            self.shift_pressed = True

    def key_up(self, key, scancode=None, *_):
        """Checks for the shift key released."""

        del key
        if scancode == 307 or scancode == 308:
            self.alt_pressed = False
        if scancode == 305 or scancode == 306:
            self.ctrl_pressed = False
        if scancode == 303 or scancode == 304:
            self.shift_pressed = False
Пример #4
0
def trayer(ops, mode, n):
    # try:

    def notify(a="0", title='Now Runnng at', tail=" mode"):
        def work():
            n.send((title,
                    a+tail))
        go(work)

    def noti(a):
        if mode[3] == 0:
            try:
                notify(a)
            except:
                pass

    def caller(a):
        def calling(sysTrayIcon, a=a):
            if 0 <= a < len(ops):
                mode[0] = a
                if mode[1] == -1:
                    mode[1] = 0
                if a < 2 and mode[2] == 1:
                    mode[2] = 0
                    noti("OCR only")
                else:
                    if mode[2] != 1:
                        noti(ops[a])
            elif a == len(ops):
                if mode[2] == 1:
                    noti("Translate only")
                    mode[2] = 0
                elif mode[2] == 0:
                    noti("OCR&Translate")
                    mode[2] = 1
            elif a == len(ops) + 1:
                if mode[1] != -1:
                    mode[1] = -1
                    noti("Listening Paused")
                else:
                    mode[1] = 0
                    noti("Listening Recoverd")
            elif a == len(ops) + 2:
                mode[3] = (mode[3] + 1) % 2
            elif a == -1:
                mode[1] = 1
                n.send(("Info", "Exiting"))

            #print(mode[0], mode[1], mode[2], mode[3])

        return lambda sysTrayIcon: go(calling, args=(sysTrayIcon,))
    menu = ()
    trans = ()
    for i, ix in enumerate(ops):
        now = (ix, None, caller(i))
        if i <= 1:
            menu += (now,)
        else:
            trans += (now,)
    menu += (("Translate", None, trans,), ("OCR&Translate", None, caller(i + 1)),
             ("Pause", None, caller(i + 2)), ("Disable Notify", None, caller(i + 3)))

    hover_text = "PowerClipboard"

    tray = SysTrayIcon("main.ico", hover_text,
                       menu_options=menu, on_quit=caller(-1), default_menu_index=1)
    # noti("!!!!")
    tray.start()
    return tray
Пример #5
0
def notify_tray():
    menu_options = (("Open GUI", None, vp_start_gui()),)
    systray = SysTrayIcon("icon/icon.ico", "Treye", menu_options)
    systray.start()   
Пример #6
0
idle_icon = current_dir + 'check-circle.ico'
idle_text = 'rsync is not running'

syncing_icon = current_dir + 'arrow-circle-up.ico'
syncing_text = 'rsync is running'

# (delays are in seconds)
check_delay = 5
start_delay = 1

##############


def rsync_checker(systray_icon):
  while True:
    cmd_status, cmd_output = getstatusoutput('tasklist | find /c /i "rsync"')
    if cmd_output == '0':
      systray_icon.update(icon=idle_icon, hover_text=idle_text)
    else:
      systray_icon.update(icon=syncing_icon, hover_text=syncing_text)
    time.sleep(check_delay)


systray_icon = SysTrayIcon(idle_icon, idle_text)
systray_icon.start()

rsync_checker_thread = threading.Timer(
    start_delay, rsync_checker, args=[systray_icon])
rsync_checker_thread.daemon = True
rsync_checker_thread.start()
Пример #7
0
class WindowsSystemTray(object):
    def __init__(self):
        self.image_dir = os.path.join(plexpy.PROG_DIR, 'data/interfaces/',
                                      plexpy.CONFIG.INTERFACE, 'images')
        self.icon = os.path.join(self.image_dir, 'logo-circle.ico')

        if plexpy.UPDATE_AVAILABLE:
            self.hover_text = common.PRODUCT + ' - Update Available!'
            self.update_title = 'Check for Updates - Update Available!'
        else:
            self.hover_text = common.PRODUCT
            self.update_title = 'Check for Updates'

        if plexpy.CONFIG.LAUNCH_STARTUP:
            launch_start_icon = os.path.join(self.image_dir, 'check-solid.ico')
        else:
            launch_start_icon = None
        if plexpy.CONFIG.LAUNCH_BROWSER:
            launch_browser_icon = os.path.join(self.image_dir,
                                               'check-solid.ico')
        else:
            launch_browser_icon = None

        self.menu = [['Open Tautulli', None, self.tray_open, 'default'],
                     ['', None, 'separator', None],
                     [
                         'Start Tautulli at Login', launch_start_icon,
                         self.tray_startup, None
                     ],
                     [
                         'Open Browser when Tautulli Starts',
                         launch_browser_icon, self.tray_browser, None
                     ], ['', None, 'separator', None],
                     [self.update_title, None, self.tray_check_update, None],
                     ['Restart', None, self.tray_restart, None]]
        if not plexpy.FROZEN:
            self.menu.insert(6, ['Update', None, self.tray_update, None])

        self.tray_icon = SysTrayIcon(self.icon,
                                     self.hover_text,
                                     self.menu,
                                     on_quit=self.tray_quit)

    def start(self):
        logger.info("Launching Windows system tray icon.")
        try:
            self.tray_icon.start()
        except Exception as e:
            logger.error("Unable to launch system tray icon: %s." % e)

    def shutdown(self):
        self.tray_icon.shutdown()

    def update(self, **kwargs):
        self.tray_icon.update(**kwargs)

    def tray_open(self, tray_icon):
        plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, plexpy.HTTP_PORT,
                              plexpy.HTTP_ROOT)

    def tray_startup(self, tray_icon):
        plexpy.CONFIG.LAUNCH_STARTUP = not plexpy.CONFIG.LAUNCH_STARTUP
        set_startup()

    def tray_browser(self, tray_icon):
        plexpy.CONFIG.LAUNCH_BROWSER = not plexpy.CONFIG.LAUNCH_BROWSER
        set_startup()

    def tray_check_update(self, tray_icon):
        versioncheck.check_update()

    def tray_update(self, tray_icon):
        if plexpy.UPDATE_AVAILABLE:
            plexpy.SIGNAL = 'update'
        else:
            self.hover_text = common.PRODUCT + ' - No Update Available'
            self.update_title = 'Check for Updates - No Update Available'
            self.menu[5][0] = self.update_title
            self.update(hover_text=self.hover_text, menu_options=self.menu)

    def tray_restart(self, tray_icon):
        plexpy.SIGNAL = 'restart'

    def tray_quit(self, tray_icon):
        plexpy.SIGNAL = 'shutdown'

    def change_tray_update_icon(self):
        if plexpy.UPDATE_AVAILABLE:
            self.hover_text = common.PRODUCT + ' - Update Available!'
            self.update_title = 'Check for Updates - Update Available!'
        else:
            self.hover_text = common.PRODUCT + ' - No Update Available'
            self.update_title = 'Check for Updates'
        self.menu[5][0] = self.update_title
        self.update(hover_text=self.hover_text, menu_options=self.menu)

    def change_tray_icons(self):
        if plexpy.CONFIG.LAUNCH_STARTUP:
            launch_start_icon = os.path.join(self.image_dir, 'check-solid.ico')
        else:
            launch_start_icon = None
        if plexpy.CONFIG.LAUNCH_BROWSER:
            launch_browser_icon = os.path.join(self.image_dir,
                                               'check-solid.ico')
        else:
            launch_browser_icon = None
        self.menu[2][1] = launch_start_icon
        self.menu[3][1] = launch_browser_icon
        self.update(menu_options=self.menu)
Пример #8
0
class TaskTray():
    def __init__(self, wallcord):
        global credit_text

        self.window = wallcord
        self.root = self.window.root
        self.version = self.window.version

        with open("data.json", "r") as f:
            self.data = load(f)
        credit_text = credit_text.replace("!version!", self.version)

        if self.version in ["1.1.1", "1.1.0", "1.0.0"]:
            messagebox.showwarning(
                "FreedomWall",
                "バージョンとFreedomWallのプログラムがあいませんでした。\n再ダウンロードしてください。")
            self.window.onoff = False
            self.window.q.append(self.exit)

        # メインスレッドじゃないとTkinterメソッドを実行できない。
        # だからメインスレッドのFreedomWallクラスのself.qに実行したいのを追加する。
        # そしてメインスレッドから実行する。
        # だから lambda がある。
        self.icon = SysTrayIcon(
            "icon.ico",
            "FreedomWall",
            (("FreedomWall", None,
              lambda sysTrayIcon: self.window.q.append(self.credit)),
             ("Set", None,
              lambda sysTrayIcon: self.window.q.append(self.setting)),
             ("Del", None,
              lambda sysTrayIcon: self.window.q.append(self.delete)),
             ("List", None,
              lambda sysTrayIcon: self.window.q.append(self.setting_list))),
            on_quit=lambda sysTrayIcon: self.window.q.append(self.exit))

    # 壁紙の設定。
    def setting(self):
        # simpledialog.askstringで入力ボックスを表示する。
        # それを使い設定をする。

        title = simpledialog.askstring("FreedomWall",
                                       "設定したいウィンドウのタイトルにある文字を入力してください。")
        if not title:
            return

        alpha = simpledialog.askstring(
            "FreedomWall",
            "壁紙の透明度を入力してください。\nデフォルトは0.2です。\n元の背景が白の場合は0.3あたりの数値が良いです。\n元の背景が黒の場合は0.1あたりの数値が良いです。"
        )
        if not alpha:
            return
        try:
            alpha = float(alpha)
        except:
            messagebox.showwarning("FreedomWall", "0.1 ~ 1 の間を設定してください。")
            return

        exception = simpledialog.askstring(
            "FreedomWall",
            "例外ウィンドウのタイトルにある文字を入力してください。\nコンマ( , )を使うことで複数追加できます。\n\n例えばLINEを登録したあとにLINEについてのウェブサイトを閲覧したとします。\nするとブラウザのタイトルにLINEが入り被りが発生します。\nそれを防ぐためにブラウザのタイトルを入れることをおすすめします。"
        )
        exception = exception.split(",") if exception else []

        # 壁紙ファイルを取得する。
        file_path = filedialog.askopenfilename(
            filetypes=[("壁紙ファイル", "*.png;*.jpg;*.mp4")])
        if not file_path:
            return

        # 存在確認をする。
        if not GetWindow(title, "bubun"):
            messagebox.showwarning("FreedomWall",
                                   f"{title}があるタイトルのウィンドウが見つかりませんでした。")
            return

        self.data["windows"][title] = {
            "path": file_path,
            "alpha": alpha,
            "exception": exception
        }
        with open("data.json", "w") as f:
            dump(self.data, f, indent=4)

        # メインスレッドのdataを再読み込みさせる。
        self.window.reload()

        self.window.now = {"path": "", "alpha": 0, "exception": []}
        messagebox.showinfo("FreedomWall", "設定しました。")

    # 壁紙の削除。
    def delete(self):
        title = simpledialog.askstring("FreedomWall", "削除したい設定名を入力してください。")
        if not title:
            return
        if not title in self.data["windows"]:
            messagebox.showwarning("FreedomWall", "その設定が見つかりませんでした。")
            return

        del self.data["windows"][title]
        with open("data.json", "w") as f:
            dump(self.data, f, indent=4)

        # メインスレッドのdataを再読み込みさせる。
        self.window.reload()

        self.window.now = {"path": "", "alpha": 0, "exception": []}
        messagebox.showinfo("FreedomWall", "その設定を削除しました。")

    # 壁紙一覧。
    def setting_list(self):
        desc = ", ".join(data for data in self.data["windows"].keys())
        messagebox.showinfo("FreedomWall", desc)

    # クレジット。
    def credit(self):
        messagebox.showinfo("FreedomWall", credit_text)

    # 終了。
    def exit(self):
        self.root.quit()
        self.window.video = None
        self.icon.shutdown()

    # 実行。
    def run(self):
        self.icon.start()
Пример #9
0
    os.startfile("CryptExcel.config")


def trading_sheet(systray):
    os.startfile("trading.xlsx")


def open_folder(systray):
    os.startfile(os.getcwd())


options_menu = (("Open folder", None, open_folder), ("Config file", None,
                                                     config_file),
                ("Trading sheet", None, trading_sheet), ("Close", None, left))
tray = SysTrayIcon("assets/icon.ico", "CryptExcel", options_menu)
tray.start()

#Other
warnings.filterwarnings("ignore", category=DeprecationWarning)
run = True


def modify():
    #Binance API
    client = Client(api_key, secret_key)
    balances = client.get_account().get('balances')
    prices = client.get_all_tickers()
    trades = []
    for pair in pairs:
        trade = (client.get_all_orders(symbol=pair, limit=1000))
        for order in trade:
Пример #10
0
    """
    assistantCommands.hide_or_show_input(True)

def quit_(systray):
    """
    Função para fechar o programa.
    """
    assistant.stop()


# Define o menu do ícone de bandeja.
menu = (
    ("Change Language",None,changeLanguage),
    ("Enable Sounds",None,enable_sounds),
    ("Disable Sounds",None,disable_sounds),
    ("Hide Input",None,hideInput),
    ("Show Input",None,showInput),
    ("Help",None,help_)
)

# Cria um ícone de bandeja.
trayIcon = SysTrayIcon(icon,trayIcon_title+" (%s)"%assistant.getLanguage().upper(),menu,on_quit=quit_)
trayIcon.start()

# Inicia o assistente.
assistant.run()

# Fecha o programa por completo.
time.sleep(1)
os._exit(0)
Пример #11
0
    volume_after = AVR.volume
    print("Volume turned down from %i to %i." %
          (volume_before + DB_OFFSET, volume_after + DB_OFFSET))
    return tray


# initialize connection
AVR = denonavr.DenonAVR(RECEIVER_ADDRESS)

# create source selection menu options
SOURCE_SELECTORS = []
for source in AVR.input_func_list:
    source_short = "".join(x for x in source if x.isalpha())
    exec("def select_%s(systray): AVR.input_func = '%s'" %
         (source_short, source))
    SOURCE_SELECTORS += [("Select %s" % source, None,
                          eval("select_%s" % source_short))]

# build up full set of tray icon menu options
MENU_OPTIONS = [
    ("Power On", None, power_on),
    ("Power Off", None, power_off),
    ("Volume Up", None, volume_up),
    ("Volume Down", None, volume_down),
] + SOURCE_SELECTORS

# create tray icon and start main loop
SYSTRAY = SysTrayIcon(os.path.join(WORK_DIR, "denon.ico"), "denontray",
                      tuple(MENU_OPTIONS))
SYSTRAY.start()
Пример #12
0
class PopUpWindow(object):
    windowCount = 0
    counter = -1

    def counterLabel(self, label):
        self.resetBtn['state'] = 'normal'
        self.stopBtn['state'] = 'normal'

        def count():
            if running:
                # To manager the intial delay
                if PopUpWindow.counter == -1:
                    display = "Starting..."
                else:
                    display = str(
                        datetime.timedelta(seconds=PopUpWindow.counter))

                label.config(text=display)
                label.after(1000, count)
                PopUpWindow.counter += 1

        # Triggering the start of the counter.
        count()

    # Start the function of the stopwatch
    def Start(self):
        global running
        running = True
        self.counterLabel(self.timerLbl)
        self.breakBtn['state'] = 'disabled'
        self.stopBtn['state'] = 'normal'
        self.resetBtn['state'] = 'normal'

    # Stop function of the stopwatch
    def Stop(self):
        global running
        running = False
        self.breakBtn['state'] = 'normal'
        self.stopBtn['state'] = 'disabled'
        self.resetBtn['state'] = 'normal'

    # Reset function of the stopwatch
    def Reset(self):
        PopUpWindow.counter = -1

        # If reset is pressed after pressing stop
        if running == False:
            self.resetBtn['state'] = 'disabled'
            self.timerLbl['text'] = 'Welcome!'

        # If reset is pressed while hte stopwatch is running
        else:
            self.timerLbl['text'] = 'Starting ...'

    def getStatus():
        return programContinue

    def exitApp(self):
        Flag.setFlag(1)
        self.systray.shutdown()
        self.window.destroy()

    def closeWindow(self):
        PopUpWindow.windowCount = PopUpWindow.windowCount + 1
        self.window.destroy()

    def increasePopUpCount(self):
        global popUpCount
        popUpCount = popUpCount + 1
        self.popUpTimer.config(text=str(popUpCount) + " Minutes")

    def decreasePopUpCount(self):
        global popUpCount
        popUpCount = popUpCount - 1
        self.popUpTimer.config(text=str(popUpCount) + " Minutes")

    def getPopUpCount():
        global popUpCount
        return popUpCount

    def say_hello(systray):
        print("Hello")

    def __init__(self, time):

        # Create a blank window
        self.window = Tk()
        self.window.title("Break Timer")

        # Create a label widget
        lbl = Label(self.window, text="The time is", font=("Arial Bold", 20))
        lbl.grid(column=0, row=0)

        # Create a label to display the current time
        self.timeLbl = Label(self.window, text=time, font=("Arial Bold", 20))
        self.timeLbl.grid(column=0, row=1)

        # Create a timer label
        if (PopUpWindow.counter == -1):
            self.timerLbl = Label(self.window,
                                  text="00:00",
                                  font=("Arial Bold", 20))
        else:
            self.timerLbl = Label(
                self.window,
                text=str(datetime.timedelta(seconds=PopUpWindow.counter)),
                font=("Arial Bold", 20))
        self.timerLbl.grid(column=1, row=1)

        # Create a button to take a break
        self.breakBtn = Button(self.window,
                               text="Take a break",
                               bg="grey80",
                               fg="black",
                               command=lambda: self.Start(),
                               font=("Arial", 20))
        self.breakBtn.grid(column=0, row=2)

        # Create a reset timer button
        self.resetBtn = Button(self.window,
                               text="Reset timer",
                               bg="grey80",
                               fg="black",
                               state="disabled",
                               command=lambda: self.Reset(),
                               font=("Arial", 20))
        self.resetBtn.grid(column=1, row=2)

        # Create a stop timer button
        self.stopBtn = Button(self.window,
                              text="Stop timer",
                              bg="grey80",
                              fg="black",
                              state="disabled",
                              command=lambda: self.Stop(),
                              font=("Arial", 20))
        self.stopBtn.grid(column=2, row=2)

        separator = Frame(height=30, bd=1, relief=SUNKEN)
        separator.grid(column=0, row=3)

        # Create a close button
        closeBtn = Button(self.window,
                          text="Close Window",
                          bg="grey80",
                          fg="black",
                          command=lambda: self.closeWindow(),
                          font=("Arial", 20))
        closeBtn.grid(column=0, row=4)

        separator2 = Frame(height=30, bd=1, relief=SUNKEN)
        separator2.grid(column=0, row=5)

        # Create a exit button
        exitBtn = Button(self.window,
                         text="Exit App",
                         bg="grey80",
                         fg="black",
                         command=lambda: self.exitApp(),
                         font=("Arial", 20))
        exitBtn.grid(column=0, row=6)

        # Create a window count label
        counterLbl = Label(self.window,
                           text="Pop up count: " +
                           str(PopUpWindow.windowCount),
                           font=("Arial Bold", 20))
        counterLbl.grid(column=1, row=0)

        # Create a label to display the amount of time until the pop-up indow pop ups
        self.popUpTimer = Label(self.window,
                                text=str(popUpCount) + " Minutes",
                                font=("Arial Bold", 20))
        self.popUpTimer.grid(column=2, row=5)

        self.popUpTimerUpButton = Button(
            self.window,
            text="+1",
            bg="grey80",
            fg="black",
            command=lambda: self.increasePopUpCount(),
            font=("Arial Bold", 20),
            width=3)
        self.popUpTimerUpButton.grid(column=2, row=4)

        self.popUpTimerDownButton = Button(
            self.window,
            text="-1",
            bg="grey80",
            fg="black",
            command=lambda: self.decreasePopUpCount(),
            font=("Arial Bold", 20),
            width=3)
        self.popUpTimerDownButton.grid(column=2, row=6)

        # set the window size
        self.window.geometry('600x350')

        # Create a message box to display the current time
        #messagebox.showinfo("Current Time", time)

        # Bring the Window in front of every other window
        self.window.attributes("-topmost", True)

        # Create a system tray icon
        menu_options = (("Reset Timer", None, self.Reset), )
        self.systray = SysTrayIcon("icon.ico", "Example tray icon",
                                   menu_options)
        self.systray.start()

        # Start the window
        self.window.mainloop()
Пример #13
0
        # Place here the code to close the reg object that reads the registry
        wr.CloseKey(openkey)
        print("Program finished")

    finally:
        # Place here the code to close the reg object that reads the registry
        wr.CloseKey(openkey)

        print("Logic thread finished")


# this is the main thread
if __name__ == "__main__":

    menu_options = (
        ("Options...", None, call_opts_tray),
        ("About AutoTheme-19", None, call_about_tray),
    )

    icon = SysTrayIcon(resource_path("icons\\16\\B16sun.ico"),
                       "AutoTheme-19",
                       menu_options,
                       on_quit=make_me_stop)

    # Start the logic thread after defining the icon, because the logic thread needs to update the icon.
    mylogic = threading.Thread(target=logic_thread)
    mylogic.start()

    # now we start the tray icon
    icon.start()
Пример #14
0
def main():
    sessions = AudioUtilities.GetAllSessions
    mixer = Mixer()

    # SysTrayIcon menu items
    menu_options = ()
    # SysTrayIcon object
    systray = SysTrayIcon(None,
                          "NK2Mixer",
                          menu_options,
                          on_quit=partial(exit_program, mixer))
    # Start SysTray
    systray.start()

    # Map physical faders to Voicemeeter faders - mappings subject to personal preference
    with voicemeeter.remote('banana', 0.0005) as vmr:
        mixer.vb_map = {
            4: vmr.inputs[3],
            5: vmr.inputs[4],
            6: vmr.inputs[1],
            7: vmr.outputs[0]
        }

    for session in sessions():
        if session.Process:
            print(session.Process.name)

    while mixer.running:
        # Receive midi message (non-blocking)
        msg = mixer.nk2_in.poll()

        # If no message exists, end current loop
        if not msg:
            continue

        # Check for select button press
        if msg.control in mixer.select_range and msg.value:
            group = mixer.groups[msg.control - mixer.select_fader_diff]

            # If program is not bound bound
            if not group.program:
                # Get active program name
                active_program = get_active_program()
                # Find audio session with matching name
                session = next(
                    (s for s in sessions()
                     if s.Process and s.Process.name() == active_program),
                    None)

                # If audio session does not exist end current loop
                if not session:
                    continue

                # Assign session to control group
                group.program = session

                # Turn on select button light
                mixer.enable_light(group.select)

                # If program is muted turn on mute button light
                if group.program.SimpleAudioVolume.GetMute():
                    mixer.enable_light(group.mute)

                print(
                    f"{group.program.Process.name()} bound to fader {group.fader}"
                )

            else:
                print(
                    f"{group.program.Process.name()} unbound from fader {group.fader}"
                )

                # Unassign session from fader
                group.program = None

                # Turn off select button light
                mixer.disable_light(group.select)
                # Turn off mute button light
                mixer.disable_light(group.mute)

        # Check for mute button press
        elif msg.control in mixer.mute_range and msg.value and mixer.groups[
                msg.control - mixer.mute_fader_diff].program:
            group = mixer.groups[msg.control - mixer.mute_fader_diff]

            # Check if program is muted
            if group.program.SimpleAudioVolume.GetMute():
                # Unmute program
                group.program.SimpleAudioVolume.SetMute(0, None)
                # Turn off mute button light
                mixer.disable_light(group.mute)

                print(
                    f"{group.program.Process.name()} unmuted (fader {group.fader})"
                )

            # If program is not muted
            else:
                # Mute program
                group.program.SimpleAudioVolume.SetMute(1, None)
                # Turn on mute button light
                mixer.enable_light(group.mute)

                print(
                    f"{group.program.Process.name()} muted (fader {group.fader})"
                )

        # Check for fader input
        elif msg.control in mixer.fader_range:
            group = mixer.groups[msg.control]

            # If fader does not have assigned program end current loop
            if not group.program:
                continue

            # Get volume control object from session
            volume = group.program._ctl.QueryInterface(ISimpleAudioVolume)
            # Convert midi value to percentage and set volume
            volume.SetMasterVolume(msg.value / 127, None)

            print(
                f"{group.program.Process.name()} set to {volume.GetMasterVolume() * 100}%"
            )

        # Check for Voicemeeter fader input
        elif msg.control in mixer.vb_fader_range:
            # Map midi value (0-127) to VB appropriate gain value (-60-0)
            level = ((127 - msg.value) / 127) * -60
            # Set VB fader gain
            mixer.vb_map[msg.control].gain = level

            print(f"fader {msg.control} (VoiceMeeter) gain set to {level}")

        elif msg.control in mixer.vb_mute_range and msg.value:
            fader = msg.control - mixer.mute_fader_diff
            control = mixer.vb_map[fader]

            # ISSUE: inconsistent mute/unmute

            if control.mute:
                # Unmute VB control
                control.mute = False
                # Turn off mute button light
                mixer.disable_light(msg.control)

                print(f"fader {fader} (VoiceMeeter) unmuted")
            else:
                # Mute FB control
                control.mute = True
                # Turn on mute button light
                mixer.enable_light(msg.control)

                print(f"fader {fader} (VoiceMeeter) muted")

        # After input is processed delete message to prevent unnecessary looping
        msg = None
Пример #15
0
class SysTrayService:
    def __init__(self, mainService):
        logger.info('Run : ini')
        self.app = None
        self.aboutUI = None
        self.settingUI = None

        self.mainService = mainService

    def start(self):
        # TODO notification anpassen (kein ToolTip)
        logger.info('Run: iniSystray')
        menu_options = (
            ("Settings", None, self.openSettings),
            ("Restart", None, self.restart),
            ("About", None, self.about),
        )
        self.systray = SysTrayIcon(CHECK_ICO,
                                   STATUS_OK,
                                   menu_options,
                                   on_quit=self.on_quit_callback)
        logger.info('systray start')
        self.systray.start()

    def about(self, systray):

        # TODO About Anpassen
        self.iniAboutUI()
        aboutText = '<html><head/><body><p>Utility to get a notification to commit and/or push the repository</p><p><br/>Developed by Christian Beigelbeck \
        </p><p>\
        Licensed under the <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html"><span style=" text-decoration:\
         underline; color:#2980b9;">GPL v3 license</span></a></p><p>Project home: \
         <a href="https://overmindstudios.github.io/BlenderUpdater/"><span style=" text-decoration:\
         underline; color:#2980b9;">https://www.github.io/VCSReminder/</a></p> \
         Application version: ' + '0.0.1' + '</body></html> '

        QtWidgets.QMessageBox.about(self.aboutUI, 'About', aboutText)

    def on_quit_callback(self, systray):
        logger.info('Run: on_quit_callback')
        self.mainService.stop()

    def updateSystrayInfo(self, ico, status):
        self.systray.update(ico, status)

    def restart(self, systray):
        logger.info('Run: restart')
        self.mainService.restart()

    def openSettings(self, systray):

        logger.info('Run: openSettings')
        self.iniSettingUI()
        logger.info('exec settingUI')
        self.settingUI.exec()
        self.app.exec_()

    def iniAboutUI(self):
        if self.app == None:
            logger.info('INI Application')
            self.app = QtWidgets.QApplication(sys.argv)
            logger.info('set Style')
            self.app.setStyle("Fusion")
            logger.info('set Palette')
            self.app.setPalette(setPalette())
        if self.aboutUI == None:
            self.aboutUI = AboutUIService()

    def iniSettingUI(self):
        if self.app == None:
            logger.info('INI Application')
            self.app = QtWidgets.QApplication(sys.argv)
            logger.info('set Style')
            self.app.setStyle("Fusion")
            logger.info('set Palette')
            self.app.setPalette(setPalette())
        if self.settingUI == None:
            self.settingUI = SettingsUIService(self.mainService)
Пример #16
0
class TrayIcon(Pipeable):
    """
    Non-blocking tray icon.

    Tray icons require blocking due to having to wait for button clicks.
    To prevent blocking on the main thread, a tread is spawned for the tray
    icon and click events are send through `Pipeable` pipes.
    """

    MenuOptionCallback = Callable[[SysTrayIcon], None]
    MenuOption = tuple[str, Optional[str], MenuOptionCallback]

    _thread: Thread
    _icon: SysTrayIcon
    _menu_options: list[MenuOption] = []
    _stop_event: Event

    _icon_image: str
    _hover_text: str

    def __init__(self, icon_image: str = "icon.ico", hover_text: str = "Hover text", stop_event: Event = Event()):
        """
        Initialize system tray icon.

        Args:
            icon_image (str, optional): Name of image to use as tray icon. Defaults to "icon.ico".
            hover_text (str, optional): Text displayed when the tray icon is hovered. Defaults to "Hover text".
            stop_event (Event, optional): Flag is set when the tray icon process exits. Defaults to Event().
        """
        super().__init__()

        self._stop_event = stop_event

        self._icon_image = icon_image
        self._hover_text = hover_text

        self._menu_options = [
            ("Show QR code", None, lambda icon: self._pipe.send("show_qr_code")),
            ("Show debug logs", None, lambda icon: self._pipe.send("show_debug_logs"))
        ]

    def run(self):
        """
        Display the tray icon in the system tray.

        Tray icon thread is spawned and the tray loop is executed.
        """
        self._icon = SysTrayIcon(
            self._icon_image, self._hover_text, tuple(self._menu_options), on_quit=self.handle_quit)

        self._thread = Thread(target=self._loop)
        self._thread.start()

    def handle_quit(self, icon: SysTrayIcon):
        """
        Set the stop flag.

        Called when the quit option is selected from the icons context menu.

        Args:
            icon (SysTrayIcon): SysTrayIcon reference
        """
        self._stop_event.set()

    def join(self):
        """Join the thread that runs the TrayIcon."""
        self._thread.join()

    def _loop(self):
        """
        Listen to events on the system tray icon.

        @NOTE This should not be run directly and will block the main thread.
        It is automatically executed on a spawned worker thread internally.
        """
        self._icon.start()

        while True:
            if self._stop_event.is_set():
                self._icon.shutdown()
                return

            sleep(SLEEP_INTERVAL)
Пример #17
0

def AskDir(systray):
    global dirname
    Dir = Tk()  # Set tkinter root
    Dir.withdraw()  # IDK stackoverflow said some
    Dir.iconbitmap('KSrcIcon.ico')  # Set icon
    tempdir = filedialog.askdirectory(
        initialdir=dirname, title='Please select a directory')  # Do some stuff
    if tempdir != "":  # Check if nothing was done
        dirname = tempdir  # They did something? Brilliant!
        dirname += "\\"  # Add this cuz python don't like me
    print("Dir set to: " + dirname)
    with open("Dir_Save", "w+") as f:  # Set the dir back to save file
        f.write(dirname)


# I don't really understand this. Just copied it from stackoverflow


def on_exit(systray):
    exit()


menu_options = (
    ("Screenshot", None, Screenshot), ("Open Folder", None, OpenFolder),
    ("Set Directory", None, AskDir))  # Some systray stuff idk
systray = SysTrayIcon("KSrcIcon.ico", "KSrc", menu_options,
                      on_quit=on_exit)  # Even more systray stuff
systray.start()  # Start them systray
Пример #18
0
    os.startfile(path)


try:
    with open("config.ini", "r") as config:
        for line in config:
            line = ''.join(line)
            start_dsc = line.find('##')
            start_file = line.rfind('/')
            filename = line[start_file + 1: start_dsc].replace(" ", "")
            path = line[:start_dsc - 1]
            description = line[start_dsc + 3:]
            new_callback_function = partial(callback_function, path=path)
            callback = new_callback_function
            iconextract(path, filename)
            if len(description) > 1:
                sep = ' || '
            else:
                sep = ' '
            softlist.append((filename + sep + description, filename + ".ico", callback))
except:
    pass


softlist.append(('Add/Del Software', 'add.ico', popupmsg))
for i in range(0, len(softlist)):
    menu_options += (softlist[i],)

sysTrayIcon = SysTrayIcon('tray.ico', hover_text, menu_options, default_menu_index=len(softlist)-1)
sysTrayIcon.start()
Пример #19
0
class MainService:
    def __init__(self, notificationService, gitService):
        self.CHECK_ICO = StringService.getIcons('success')
        self.ERROR_ICO = StringService.getIcons('error')
        self.CHANGE_ICO = StringService.getIcons('warning')

        self.notificationService = notificationService
        self.gitService = gitService
        self.isGitReminderStarted = False
        self.threads = []

        self.countStatusDirty = 0
        self.countStatusError = 0
        self.countStatusOk = 0

    def startSystray(self):
        a = StringService.getIcons('success')

        try:
            menu_options = (
                (APP_NAME, self.CHECK_ICO, (
                    ('Start', None, self.startGitReminderFromSystray),
                    ('Stop', None, self.stopGitReminderFromSystray),
                    ('Restart', None, self.restartGitReminderFromSystray),
                )),
                ("Status", None, self.status),
                ("About", None, self.about),
            )
            self.systray = SysTrayIcon(self.CHECK_ICO,
                                       STATUS_OK,
                                       menu_options,
                                       on_quit=self.on_quit_callback)
            self.systray.start()
        except:
            self.notificationService.showToastNotification(
                APP_NAME, "Start: FAILED", self.ERROR_ICO)
            sys.exit()

    def startGitReminderFromSystray(self, systray):
        self.startGitReminder()

    def startGitReminder(self):
        if self.isGitReminderStarted:
            self.notificationService.showToastNotification(
                APP_NAME, "is already started", self.CHECK_ICO)
        else:
            configService = ConfigService(self.notificationService)
            self.noGitRepos = False
            self.dirtyGitRepos = False

            self.countStatusDirty = 0
            self.countStatusError = 0
            self.countStatusOk = 0

            for repo in configService.readConf():
                if self.gitService.isGitRepo(repo):
                    if self.gitService.isRepoDirty(repo):
                        self.countStatusDirty += 1
                        self.updateSystrayInfo()
                        self.startThreadwithRepo(repo)
                    else:
                        self.countStatusOk += 1
                        self.startThreadwithRepo(repo)
                        self.updateSystrayInfo()
                else:
                    self.countStatusError += 1
                    self.updateSystrayInfo()

            self.notificationService.showToastNotification(
                APP_NAME, "is started", self.CHECK_ICO)

            self.isGitReminderStarted = True

    def startThreadwithRepo(self, repo):
        thread = Thread(target=self.profileThreads, args=(repo, ))
        self.threads += [thread]
        thread.start()

    def profileThreads(self, repo):
        mins = int(repo.sleeptime) * 60
        currentTime = 0
        while self.isGitReminderStarted:
            if currentTime == mins:
                nowTime = datetime.now()
                targetTime = datetime(nowTime.year, nowTime.month, nowTime.day,
                                      int(profile.reminderTimeHour),
                                      int(profile.reminderTimeMin))
                if targetTime < nowTime:
                    self.startCheckProfile(profile)
                currentTime = 0

            time.sleep(1)
            currentTime += 1

    def stopGitReminderFromSystray(self, systray):
        self.stopGitReminder()

    def stopGitReminder(self):
        if not self.isGitReminderStarted:
            self.notificationService.showToastNotification(
                APP_NAME, "is already stopped", self.CHECK_ICO)
        else:
            self.isGitReminderStarted = False
            for x in self.threads:
                x.join()
            self.threads = []
            self.notificationService.showToastNotification(
                APP_NAME, "is stopped", self.CHECK_ICO)
            self.systrayUpdate(CHECK_ICO, STATUS_NOT_RUNNING)

    def restartGitReminder(self):
        self.stopGitReminder()
        self.startGitReminder()

    def restartGitReminderFromSystray(self, systray):
        self.restartGitReminder()

    def status(self, systray):
        if self.isGitReminderStarted:
            self.notificationService.showToastNotification(
                APP_NAME, "is started", self.CHECK_ICO)
        else:
            self.notificationService.showToastNotification(
                APP_NAME, "is stopped", self.CHECK_ICO)

    def updateSystrayInfo(self):
        if self.countStatusError > 0:
            self.systrayUpdate(
                self.ERROR_ICO,
                StringService.getMessages('systray_status_error').format(
                    self.countStatusError))
        elif self.countStatusDirty > 0:
            self.systrayUpdate(
                self.CHANGE_ICO,
                StringService.getMessages('systray_status_dirty').format(
                    self.countStatusDirty))
        else:
            self.systrayUpdate(
                self.CHECK_ICO,
                StringService.getMessages('systray_status_ok').format(
                    self.countStatusOk))

    def systrayUpdate(self, ico, status):
        self.systray._create_window
        self.systray.update(ico, status)

    def about(self, systray):
        webbrowser.open(StringService.getMetaInfos('about'))

    def on_quit_callback(self, systray):
        pass
Пример #20
0
class Interface(Frame):
    def __init__(self, master=None, data={}):
        Frame.__init__(self, master)
        self.version = "1.2.0"
        self.master.title(data["window_name"])
        self.data = data
        self._main = Main(self.data["directory_name"])
        self.default = StringVar()
        self.default.set(self._main.language)
        self.load_config()
        self.init_elements()
        self.fill_from_config()

        menu_options = (("Open Window", None, self.back), ("Start", None,
                                                           self.start_service),
                        ("Stop", None, self.stop_service))
        self.systray = SysTrayIcon("./{0}/icon.ico".format(
            self.data["directory_name"]),
                                   "SIVA",
                                   menu_options,
                                   on_quit=self.quit_service)
        self.systray.start()
        self.master.protocol("WM_DELETE_WINDOW", self.vanish)

    def quit_service(self, e=None):
        exit(0)

    def vanish(self):
        self.master.withdraw()

    def back(self, e=None):
        self.master.update()
        self.master.deiconify()

    def load_config(self):
        if path.isfile("./{0}/config.json".format(
                self.data["directory_name"])):
            with open("./{0}/config.json".format(self.data["directory_name"]),
                      "r") as out:
                self.config = json.load(out)
        else:
            with open("./{0}/config.json".format(self.data["directory_name"]),
                      "w+") as out:
                self.config = {
                    "api_token": "",
                    "platform": "Playstation",
                    "username": "",
                    "language": "en",
                    "autostart": False,
                    "id_search": False
                }
                json.dump(self.config, out, indent=4)

    def fill_from_config(self):
        self.token_box.delete(0, END)
        self.token_box.insert(0, self.config["api_token"])

        self.option_menu_default.set(self.config["platform"])

        self.username_box.delete(0, END)
        self.username_box.insert(0, self.config["username"])

        if self.config.get("autostart", None) == None:
            self.config["autostart"] = False
        if self.config.get("autostart", None) == True:
            self.start_service()
        if self.config.get("id_search", None) == None:
            self.config["id_search"] = False
        self.search_with_id.set(self.config["id_search"])

    def init_elements(self):
        self.menubar = Menu(self.master)
        self.master.config(menu=self.menubar)

        self.menu_dropdown_siva = Menu(self.menubar)
        self.menu_dropdown_themes = Menu(self.menubar)
        self.menu_dropdown_links = Menu(self.menubar)
        self.menu_dropdown_help = Menu(self.menubar)
        self.menu_dropdown_language = Menu(self.menubar)

        self.menu_dropdown_siva.add_command(
            label="Start", command=lambda: self.start_service())
        self.menu_dropdown_siva.add_command(
            label="Stop", command=lambda: self.stop_service())

        self.auto_start = BooleanVar()
        self.auto_start.set(self.config.get("autostart", False))
        self.menu_dropdown_siva.add_checkbutton(
            label="Autostart",
            onvalue=True,
            offvalue=False,
            variable=self.auto_start,
            command=lambda: self.start_service())

        self.search_with_id = BooleanVar()
        self.search_with_id.set(self.config.get("id_search", False))
        self.menu_dropdown_siva.add_checkbutton(label="Login With ID",
                                                onvalue=True,
                                                offvalue=False,
                                                variable=self.search_with_id)

        self.menu_dropdown_themes.add_command(
            label="Light Theme", command=lambda: self.light_mode())
        self.menu_dropdown_themes.add_command(label="Dark Theme",
                                              command=lambda: self.dark_mode())

        self.menu_dropdown_links.add_command(
            label="Get A Token",
            command=lambda: open_new_tab(
                "https://www.bungie.net/en/Application"))
        self.menu_dropdown_links.add_command(
            label="Message Me On Reddit",
            command=lambda: open_new_tab(
                "https://www.reddit.com/message/compose?to=TheTimebike&subject=SIVA"
            ))
        self.menu_dropdown_links.add_command(
            label="Github",
            command=lambda: open_new_tab("https://github.com/TheTimebike/SIVA"
                                         ))
        self.menu_dropdown_links.add_command(
            label="Report An Issue",
            command=lambda: open_new_tab(
                "https://github.com/TheTimebike/SIVA/issues"))

        self.menu_dropdown_help.add_command(
            label="About",
            command=lambda: messagebox.showinfo(
                "SIVA", "SIVA:\nVersion: {0}\nCreator: u/TheTimebike".format(
                    self.version)))

        language_conversion_table = self.get_conversion_table("language")

        for lang, key in language_conversion_table.items():
            self.add_language(lang, key)

        self.menubar.add_cascade(label="SIVA", menu=self.menu_dropdown_siva)
        self.menubar.add_cascade(label="Themes",
                                 menu=self.menu_dropdown_themes)
        self.menubar.add_cascade(label="Links", menu=self.menu_dropdown_links)
        self.menubar.add_cascade(label="Help", menu=self.menu_dropdown_help)
        self.menubar.add_cascade(label="Languages",
                                 menu=self.menu_dropdown_language)

        if self.data["version"] != self.version:
            self.menubar.add_command(label="Update",
                                     command=lambda: update(self))

        self.label_1 = Label(self.master, text="API Token")
        self.label_1.place(x=315, y=10)

        self.token_box = Entry(self.master, width=50)
        self.token_box.place(x=10, y=10)

        self.token_button = Button(
            self.master,
            width=20,
            height=1,
            text="How Do I Find This?",
            command=lambda: open_new_tab(
                "https://www.bungie.net/en/Application"))
        self.token_button.place(x=375, y=9)

        self.start_button = Button(self.master,
                                   width=72,
                                   height=5,
                                   text="Start!",
                                   command=lambda: self.start_service())
        self.start_button.place(x=10, y=70)

        self.option_menu_default = StringVar()
        self.option_menu_default.set("Playstation")
        self.option_menu = OptionMenu(self.master, self.option_menu_default,
                                      "Playstation", "Xbox", "Steam", "Stadia")
        self.option_menu.configure(highlightthickness=0)
        self.option_menu.place(x=8, y=35)

        self.label_2 = Label(self.master, text="Select Platform")
        self.label_2.place(x=110, y=40)

        self.username_box = Entry(self.master, width=40)
        self.username_box.place(x=220, y=40)

        self.label_3 = Label(self.master, text="Username")
        self.label_3.place(x=460, y=40)

        self.elements = {
            "labels": [self.label_1, self.label_2, self.label_3],
            "entrys": [self.username_box, self.token_box],
            "optionmenus": [self.option_menu],
            "buttons": [self.start_button, self.token_button]
        }

    def add_language(self, lang, key):
        self.menu_dropdown_language.add_radiobutton(
            label=lang,
            value=key,
            variable=self.default,
            command=lambda: self.change_language(key))

    def change_language(self, key):
        self._main.language = key
        self._main.configurator.save({
            "api_token": self.token_box.get(),
            "platform": self.option_menu_default.get(),
            "username": self.username_box.get(),
            "language": self._main.language,
            "autostart": self.auto_start.get()
        })

    def light_mode(self):
        text_colour = "black"
        box_colour = "white"
        background_colour = "#f0f0f0"
        self.master.configure(background=background_colour)
        for element in self.elements["labels"]:
            element.configure(background=background_colour,
                              foreground=text_colour)
        for element in self.elements["buttons"]:
            element.configure(background=background_colour,
                              foreground=text_colour)
        for element in self.elements["entrys"]:
            element.configure(background=box_colour, foreground=text_colour)
        for element in self.elements["optionmenus"]:
            element.configure(highlightthickness=0,
                              background=background_colour,
                              foreground=text_colour,
                              activebackground=background_colour,
                              activeforeground=text_colour)

    def dark_mode(self):
        text_colour = "white"
        box_colour = "#484b52"
        background_colour = "#36393f"
        self.master.configure(background=background_colour)
        for element in self.elements["labels"]:
            element.configure(background=background_colour,
                              foreground=text_colour)
        for element in self.elements["buttons"]:
            element.configure(background=background_colour,
                              foreground=text_colour)
        for element in self.elements["entrys"]:
            element.configure(background=box_colour, foreground=text_colour)
        for element in self.elements["optionmenus"]:
            element.configure(highlightthickness=0,
                              background=background_colour,
                              foreground=text_colour,
                              activebackground=background_colour,
                              activeforeground=text_colour)

    def start_service(self, e=None):
        self._main.configurator.save({
            "api_token": self.token_box.get(),
            "platform": self.option_menu_default.get(),
            "username": self.username_box.get(),
            "language": self._main.language,
            "autostart": self.auto_start.get(),
            "id_search": self.search_with_id.get()
        })
        self.thread = Thread(target=self._main.start_siva, args=(self, ))
        self.thread.daemon = True
        self.thread.start()

        self.start_button.config(text="Stop!",
                                 command=lambda: self.stop_service())

    def stop_service(self, e=None):
        self._main.run = False
        self.start_button.config(text="Start!",
                                 command=lambda: self.start_service())

    def get_conversion_table(self, table):
        _index = get(
            "https://raw.githubusercontent.com/TheTimebike/SIVA/master/conversion_tables/index.json"
        )
        _data_url = _index.json()[table]
        _data = get(_data_url)
        return _data.json()

    def error(self, error_enum):
        self.stop_service()
        error_conversion_table = self.get_conversion_table("error")
        messagebox.showinfo(error_conversion_table["error_window_name"],
                            error_conversion_table["errors"][error_enum])
        return None

    def create_pick_account_interface(self, acc_list):
        self.select_acc_window = SubWindow(self.master, acc_list, self._main)
        self.select_acc_window.master.iconbitmap("./{0}/icon.ico".format(
            self.data["directory_name"]))
Пример #21
0
class TrayIcon:
    def __init__(self):
        menuOptions = (('Audio', None, (('Select Alert Sound', None,
                                         self._onSelectAlertSound),
                                        ('Play', None, self._onPlayAlert),
                                        ('Stop', None, self._onStopAlert))),
                       ('Open Maps File', None, self._onOpenMapsFile),
                       ('Select Path of Exile folder', None,
                        self._onSelectPathOfExileDirectory))
        self._icon = SysTrayIcon(iconPath,
                                 'Map Alert',
                                 menu_options=menuOptions,
                                 on_quit=self._onQuit)
        self._callbackQueue = queue.Queue()

    async def show(self):
        self._icon.start()
        while True:
            try:
                callback = self._callbackQueue.get(False)
                callback()
            except queue.Empty:
                await asyncio.sleep(0.1)

    def close(self):
        self._icon.shutdown()

    def onSelectAlertSound(self):
        pass

    def onPlayAlert(self):
        pass

    def onStopAlert(self):
        pass

    def onOpenMapsFile(self):
        pass

    def onSelectPathOfExileDirectory(self):
        pass

    def onQuit(self):
        pass

    def _onSelectAlertSound(self, sysTray):
        self._callbackQueue.put(self.onSelectAlertSound)

    def _onPlayAlert(self, sysTray):
        self._callbackQueue.put(self.onPlayAlert)

    def _onStopAlert(self, sysTray):
        self._callbackQueue.put(self.onStopAlert)

    def _onOpenMapsFile(self, sysTray):
        self._callbackQueue.put(self.onOpenMapsFile)

    def _onSelectPathOfExileDirectory(self, sysTray):
        self._callbackQueue.put(self.onSelectPathOfExileDirectory)

    def _onQuit(self, sysTray):
        self._callbackQueue.put(sysTray.shutdown)
        self._callbackQueue.put(self.onQuit)
Пример #22
0
class GW2RPC:
    def __init__(self):
        def fetch_registry():
            url = GW2RPC_BASE_URL + "registry"
            res = requests.get(url)
            if res.status_code != 200:
                log.error("Could not fetch the web registry")
                return None
            return res.json()

        def icon_path():
            try:
                return os.path.join(sys._MEIPASS, "icon.ico")
            except:
                return "icon.ico"

        def fetch_support_invite():
            try:
                return requests.get(GW2RPC_BASE_URL +
                                    "support").json()["support"]
            except:
                return None

        self.rpc = DiscordRPC(GW2RPC_APP_ID)
        self.game = MumbleData()
        self.registry = fetch_registry()
        self.support_invite = fetch_support_invite()
        menu_options = (("About", None, self.about), )
        if self.support_invite:
            menu_options += (("Join support server", None, self.join_guild), )
        self.systray = SysTrayIcon(
            icon_path(),
            "Guild Wars 2 with Discord",
            menu_options,
            on_quit=self.shutdown)
        self.systray.start()
        self.process = None
        self.last_map_info = None
        self.last_continent_info = None
        self.last_boss = None
        self.boss_timestamp = None
        self.no_pois = set()
        self.check_for_updates()

    def shutdown(self, _=None):
        os._exit(0)  # Nuclear option

    def about(self, _):
        message = (
            "Version: {}\n\nhttps://gw2rpc.info\n\nBy Maselkov & "
            "N1TR0\nIcons by Zebban\nWebsite by Penemue".format(VERSION))
        threading.Thread(target=create_msgbox, args=[message]).start()

    def join_guild(self, _):
        try:
            webbrowser.open(self.support_invite)
        except webbrowser.Error:
            pass

    def check_for_updates(self):
        def get_build():
            url = GW2RPC_BASE_URL + "build"
            r = requests.get(url)
            try:
                return r.json()["build"]
            except:
                return None

        build = get_build()
        if not build:
            log.error("Could not retrieve build!")
            create_msgbox(
                "Could not check for updates - check your connection!")
            return
        if build > VERSION:
            log.info("New version found! Current: {} New: {}".format(
                VERSION, build))
            res = create_msgbox(
                "There is a new update for GW2 Rich Presence available. "
                "Would you like to be taken to the download page now?",
                code=68)
            if res == 6:
                webbrowser.open("https://gw2rpc.info/")

    def get_map_asset(self, map_info):
        map_id = map_info["id"]
        map_name = map_info["name"]
        region = map_info.get("region_name", "thanks_anet")
        if self.registry:
            if map_name == "Fractales des Brumes":
                for fractal in self.registry["fractals"]:
                    if fractal["id"] == map_id:
                        state = fractal["name"] + " fractal"
                        image = "fotm"
                        break
                else:
                    image = "fotm"
                    state = "Fractales des Brumes"
                name = "Fractales des Brumes"
            else:
                if map_name in self.registry["special"]:
                    image = self.registry["special"][map_name]
                elif map_id in self.registry["valid"]:
                    image = map_id
                elif region in self.registry["regions"]:
                    image = self.registry["regions"][region]
                else:
                    image = "default"
                name = map_name
                state = name
        else:
            special = {
                "Fractals of the Mists": "fotm",
                "Refuge de Chassevent": "gh_haven",
                "Caverne dorée": "gh_hollow",
                "Précipice perdu": "gh_precipice"
            }.get(map_info["name"])
            if special:
                return special
            if map_info["type"] == "Public":
                image = map_id
            else:
                valid_ids = [1062, 1149, 1156, 38, 1264]
                if map_id in valid_ids:
                    image = map_id
                else:
                    image = "default"
            name = map_name
            state = name
        return "dans " + state, {"large_image": str(image), "large_text": name}

    def get_raid_assets(self, map_info):
        def readable_id(_id):
            _id = _id.split("_")
            dont_capitalize = ("of", "the", "in")
            return " ".join([
                x.capitalize() if x not in dont_capitalize else x for x in _id
            ])

        boss = self.find_closest_boss(map_info)
        if not boss:
            self.boss_timestamp = None
            return self.get_map_asset(map_info)
        if boss["type"] == "boss":
            state = "en combat "
        else:
            state = "combat terminé "

        name = readable_id(boss["id"])
        state += name
        if self.last_boss != boss["id"]:
            self.boss_timestamp = int(time.time())
        self.last_boss = boss["id"]
        return state, {
            "large_image": boss["id"],
            "large_text": name + " - {}".format(map_info["name"])
        }

    def get_activity(self):
        def get_region():
            world = api.world
            if world:
                for k, v in worlds.items():
                    if world in v:
                        return " [{}]".format(k)
            return ""

        def get_closest_poi(map_info, continent_info):
            region = map_info.get("region_name")
            if config.disable_pois:
                return None
            if config.disable_pois_in_wvw and region == "World vs. World":
                return None
            return self.find_closest_point(map_info, continent_info)

        data = self.game.get_mumble_data()
        if not data:
            return None
        map_id = data["map_id"]
        try:
            if self.last_map_info and map_id == self.last_map_info["id"]:
                map_info = self.last_map_info
            else:
                map_info = api.get_map_info(map_id)
                self.last_map_info = map_info
            character = Character(data)
        except APIError:
            log.exception("API Erreur!")
            self.last_map_info = None
            return None
        state, map_asset = self.get_map_asset(map_info)
        tag = character.guild_tag if config.display_tag else ""
        try:
            if map_id in self.no_pois or "continent_id" not in map_info:
                raise APIError(404)
            if (self.last_continent_info
                    and map_id == self.last_continent_info["id"]):
                continent_info = self.last_continent_info
            else:
                continent_info = api.get_continent_info(map_info)
                self.last_continent_info = continent_info
        except APIError:
            self.last_continent_info = None
            self.no_pois.add(map_id)
        details = character.name + tag
        timestamp = self.game.last_timestamp
        if self.registry and str(map_id) in self.registry.get("raids", {}):
            state, map_asset = self.get_raid_assets(map_info)
            timestamp = self.boss_timestamp or self.game.last_timestamp
        else:
            self.last_boss = None
            if self.last_continent_info:
                point = get_closest_poi(map_info, continent_info)
                if point:
                    map_asset["large_text"] += " proche de " + point["name"]
        map_asset["large_text"] += get_region()
        activiy = {
            "state": state,
            "details": details,
            "timestamps": {
                'start': timestamp
            },
            "assets": {
                **map_asset, "small_image": character.profession_icon,
                "small_text": "{0.race} {0.profession}".format(character, tag)
            }
        }
        return activiy

    def in_character_selection(self):
        activity = {
            "state": "sélection de personnage",
            "assets": {
                "large_image":
                "default",
                "large_text":
                "Sélection de personnage",
                "small_image":
                "gw2rpclogo",
                "small_text":
                "GW2RPC Version {}\nhttps://gw2rpc.info".format(VERSION)
            }
        }
        return activity

    def convert_mumble_coordinates(self, map_info, position):
        crect = map_info["continent_rect"]
        mrect = map_info["map_rect"]
        x = crect[0][0] + (position.x - mrect[0][0]) / 24
        y = crect[0][1] + (mrect[1][1] - position.y) / 24
        return x, y

    def find_closest_point(self, map_info, continent_info):
        position = self.game.get_position()
        x_coord, y_coord = self.convert_mumble_coordinates(map_info, position)
        lowest_distance = float("inf")
        point = None
        for item in continent_info["points_of_interest"].values():
            if "name" not in item:
                continue
            distance = (item["coord"][0] - x_coord)**2 + (
                item["coord"][1] - y_coord)**2
            if distance < lowest_distance:
                lowest_distance = distance
                point = item
        return point

    def find_closest_boss(self, map_info):
        position = self.game.get_position()
        x_coord, y_coord = self.convert_mumble_coordinates(map_info, position)
        closest = None
        for boss in self.registry["raids"][str(map_info["id"])]:
            distance = math.sqrt((boss["coord"][0] - x_coord)**2 +
                                 (boss["coord"][1] - y_coord)**2)
            if "radius" in boss and distance < boss["radius"]:
                if "height" in boss:
                    if position.z < boss["height"]:
                        closest = boss
                else:
                    closest = boss
        return closest

    def main_loop(self):
        def update_gw2_process():
            shutdown = False
            if self.process:
                if self.process.is_running():
                    return
                else:
                    if config.close_with_gw2:
                        shutdown = True
            for process in psutil.process_iter(attrs=['name']):
                name = process.info['name']
                if name in ("Gw2-64.exe", "Gw2.exe"):
                    self.process = process
                    return
            if shutdown:
                self.shutdown()
            self.process = None
            raise GameNotRunningError

        def start_rpc():
            while True:
                try:
                    self.rpc.start()
                    break
                except (FileNotFoundError, PermissionError) as e:
                    time.sleep(10)

        try:
            while True:
                try:
                    update_gw2_process()
                    if not self.game.memfile:
                        self.game.create_map()
                    if not self.rpc.running:
                        start_rpc()
                        log.debug("Lancement self.rpc")
                    try:
                        data = self.get_activity()
                    except requests.exceptions.ConnectionError:
                        raise GameNotRunningError
                    if not data:
                        data = self.in_character_selection()
                    log.debug(data)
                    try:
                        self.rpc.send_rich_presence(data, self.process.pid)
                    except BrokenPipeError:
                        raise GameNotRunningError  # To start a new connection
                except GameNotRunningError:
                    #  TODO
                    self.game.close_map()
                    if self.rpc.running:
                        self.rpc.close()
                        log.debug("Killing RPC")
                time.sleep(15)
        except Exception as e:
            log.critical("GW2RPC a crashé", exc_info=e)
            create_msgbox(
                "GW2 Rich Presence a crashé.\nVérifiez le fichier "
                "log et envoyez son contenu au créateur!",
                code=16)
            self.shutdown()
Пример #23
0
class SysTrayPlugin(plugins.SimplePlugin):
    '''
    CherryPy plugin that creates a system tray icon for Windows.

    Because SysTrayIcon always fires off on_quit, we can't have on_quit
        execute cherrypy.engine.exit() if cherrypy.engine.exit() is what
        triggered SysTrayIcon to close. So conditions are set to only fire
        on_quit when the quit_method == 'men'.

    This way, when the menu option is called, it destroys SysTrayIcon then
        closes cherrypy. Cherrypy will try to close SysTrayIcon by calling
        stop(), so _on_quit() gets reassigned to None.

    If the app is closed by cherrypy (whether catching a kb interrupt or the GUI
        shutdown button), cherrypy stops the plugin by calling stop(). Stop()
        reassigns SysTrayIcon._on_quit to None and calls SysTrayIcon.shutdown().
        SysTrayIcon is then destroyed (twice for reasons I can't figure out),
        then cherrypy finishes up the engine.stop() and engine.exit().

    The chain is as such:

    Trigger == systray menu 'Quit':
    SysTrayIcon._destroy() >
    SysTrayIcon._on_quit() > set SysTrayPlugin.quit_method = 'men'
    cherrypy.engine.exit() >
    SysTrayPlugin.stop()   > does nothing
    sys.exit()

    Trigger == KBInterrupt or GUI Shutdown:
    cherrypy.engine.stop() >
    SysTrayPlugin.stop()   > disable SysTrayIcon._on_quit()
    SysTrayIcon.shutdown() >
    SysTrayIcon._destroy() >
    SysTrayIcon._destroy() >
    cherrypy.engine.exit() >
    sys.exit()

    '''

    def __init__(self, bus, icon, name, menu_options, on_quit=lambda: None):
        if not callable(on_quit):
            raise TypeError('on_quit not a callable object.')
        self.on_quit = on_quit

        plugins.SimplePlugin.__init__(self, bus)
        self.systray = SysTrayIcon(icon, name, menu_options, on_quit=self._on_quit)
        self.quit_method = None
        return

    def start(self):
        self.systray.start()
        return

    def stop(self):
        if self.quit_method == 'men':
            return
        else:
            self.systray._on_quit = None
            self.systray.shutdown()
            return

    def _on_quit(self, systray):
        self.on_quit()
        self.quit_method = 'men'
        cherrypy.engine.exit()
        sys.exit(0)
Пример #24
0
class ProjectApp(App):
    def build(self):
        # delete unnecessary default config file to avoid overloading App.get_application_config
        try:
            remove('project.ini')
        except:
            pass
        # check if config file in app dir exists
        self.custom_config_name = 'pppm.ini'
        self.app_folder = dirname(realpath(__file__))
        self.app_folder_config_fn = join(self.app_folder,
                                         self.custom_config_name)
        if isfile(self.app_folder_config_fn):
            # config is in app dir
            self.config.filename = self.app_folder_config_fn
        else:
            # config is in user dir
            self.config.filename = join(self.user_data_dir,
                                        self.custom_config_name)
        # title
        self.title = '3PM'
        # initialize settings
        self.use_kivy_settings = False
        self.settings_cls = SettingsWithTabbedPanel
        self.icon = join('data', 'icon.ico')
        # read configuration file
        self.config.read(self.config.filename)
        # initialize projects
        self.projects = Projects(name='projects', config=self.config)
        self.load_projects()
        # initialize ebs history
        self.velocity_history = []
        self.load_velocity_history()
        # initialize timer
        self.timer = Timer(self.config)
        self.simulation_string = StringProperty()
        # screen management and transition
        self.transition = SlideTransition(duration=.35)
        root = ScreenManager(transition=self.transition)
        root.add_widget(self.projects)
        return root

    def build_config(self, config):
        if platform in ['android', 'ios']:
            # smartphone defaults
            config.setdefaults(
                'timer', {
                    'start_sound': 1,
                    'end_sound': 1,
                    'vibrate': 1,
                    'notification': 0,
                    'notification_timeout': 10,
                    'session_length': 25,
                    'use_notepad': 0
                })
            config.setdefaults('ebs', {
                'use_ebs': 1,
                'number_history': 30,
                'log_activity': 0
            })
            config.setdefaults('system', {
                'enable_tray': 0,
                'hide_window': 0,
                'store_in_app': 0
            })
        else:
            # defaults for desktop computers
            config.setdefaults(
                'timer', {
                    'start_sound': 1,
                    'end_sound': 1,
                    'vibrate': 0,
                    'notification': 1,
                    'notification_timeout': 10,
                    'session_length': 25,
                    'use_notepad': 1
                })
            config.setdefaults('ebs', {
                'use_ebs': 1,
                'number_history': 30,
                'log_activity': 1
            })
            config.setdefaults('system', {
                'enable_tray': 1,
                'hide_window': 0,
                'store_in_app': 0
            })

    def build_settings(self, settings):
        settings.add_json_panel('Timer',
                                self.config,
                                data=settings_info.timer_settings_json)
        settings.add_json_panel('Simulation',
                                self.config,
                                data=settings_info.ebs_settings_json)
        settings.add_json_panel('System',
                                self.config,
                                data=settings_info.system_settings_json)

    def on_config_change(self, config, section, key, value):
        # react on store_in_app config change
        if section == 'system' and key == 'store_in_app':
            if value == '0':
                try:
                    # move data to user folder
                    move_if_exists(join(self.app_folder, 'projects.json'),
                                   join(self.user_data_dir, 'projects.json'))
                    move_if_exists(
                        join(self.app_folder, 'velocity_history.json'),
                        join(self.user_data_dir, 'velocity_history.json'))
                    move_if_exists(
                        join(self.app_folder, 'daily_activity.txt'),
                        join(self.user_data_dir, 'daily_activity.txt'))
                    # write config to user folder
                    self.config.filename = join(self.user_data_dir,
                                                self.custom_config_name)
                    self.config.write()
                    # remove old config file
                    remove(self.app_folder_config_fn)
                except:
                    # failed to move to new dir, undo config change
                    self.config.set('system', 'store_in_app', '1')
            else:
                try:
                    # move data to app folder
                    move_if_exists(join(self.user_data_dir, 'projects.json'),
                                   join(self.app_folder, 'projects.json'))
                    move_if_exists(
                        join(self.user_data_dir, 'velocity_history.json'),
                        join(self.app_folder, 'velocity_history.json'))
                    move_if_exists(
                        join(self.user_data_dir, 'daily_activity.txt'),
                        join(self.app_folder, 'daily_activity.txt'))
                    # write config to app folder
                    self.config.filename = join(self.app_folder,
                                                self.custom_config_name)
                    self.config.write()
                    # remove old config file
                    remove(join(self.user_data_dir, self.custom_config_name))
                except:
                    # failed to move to new dir, undo config change
                    self.config.set('system', 'store_in_app', '0')

        # react on enable_tray config change
        if section == 'system' and key == 'enable_tray' and platform == 'win':
            if value == '0':
                # shutdown tray icon
                try:
                    self.systray.shutdown()
                except:
                    pass
            else:
                # initialize system tray - todo: encapsulate
                menu_options = (("Show Session Info",
                                 "Show current task and remaining time.",
                                 self.systray_show_info), )
                hover_text = "Personal Project Productivity Manager - 3PM"
                self.systray = SysTrayIcon("data/icon.ico",
                                           hover_text,
                                           menu_options,
                                           default_menu_index=0,
                                           on_quit=self.systray_close_window)
                self.systray.start()

        # reinitialize timer
        self.timer.init(config)
        # reinitialize projects
        self.projects = Projects(name='projects', config=config)
        self.load_projects()
        # update projects view config
        self.projects.use_ebs = config.get('ebs', 'use_ebs') == '1'
        self.root.remove_widget(self.root.get_screen('projects'))
        self.root.add_widget(self.projects)
        self.root.current = 'projects'

    def on_start(self):
        # no current project index
        self.current_project_index = -1
        if platform == 'win' and self.config.get('system',
                                                 'enable_tray') == '1':
            # initialize system tray - todo: encapsulate
            menu_options = (("Show Session Info",
                             "Show current task and remaining time.",
                             self.systray_show_info), )
            hover_text = "Personal Project Productivity Manager - 3PM"
            self.systray = SysTrayIcon("data/icon.ico",
                                       hover_text,
                                       menu_options,
                                       default_menu_index=0,
                                       on_quit=self.systray_close_window)
            self.systray.start()

    def on_stop(self):
        self.config.write()
        if platform == 'win':
            # shutdown tray icon
            try:
                self.systray.shutdown()
            except:
                pass

    def systray_show_info(self, sysTrayIcon):
        # remaining time
        if self.timer.running_down:
            message_time = '%s remaining.' % self.timer.time_string
        elif self.timer.running_up:
            message_time = 'On break since %s.' % self.timer.time_string
        else:
            message_time = 'No session running.'
        # project string
        if self.current_project_index == -1:
            project_title = 'Quick Session'
        else:
            project_title = self.projects.data[
                self.current_project_index]['title']
        # show notification
        self.timer.notification_wrapper.notify(
            title=project_title,
            message=message_time,
            timeout=self.timer.notification_timeout)

    def systray_close_window(self, sysTrayIcon):
        # stop app if not called from traybar menu (not settings change)
        if self.config.get('system', 'enable_tray') == '1':
            try:
                App.get_running_app().stop()
            except:
                exit(0)

    def load_velocity_history(self):
        # load velocity history from file
        if isfile(self.velocity_history_fn):
            with open(self.velocity_history_fn) as fd:
                self.velocity_history = json.load(fd)
        else:
            # assume bad estimation
            self.velocity_history = [
                1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9
            ]

    def save_velocity_history(self):
        # save velocity history to file
        with open(self.velocity_history_fn, 'w') as fd:
            json.dump(self.velocity_history, fd)

    def load_projects(self):
        # load projects from file
        if not isfile(self.projects_fn):
            return
        with open(self.projects_fn) as fd:
            data = json.load(fd)
        self.projects.data = data

    def save_projects(self):
        # save projects to file
        with open(self.projects_fn, 'w') as fd:
            json.dump(self.projects.data, fd)

    def delete_project(self, project_index):
        # go to project list
        self.go_projects(project_index)
        # delete project
        del self.projects.data[project_index]
        self.save_projects()
        self.refresh_projects()

    def finish_project(self, project_index):
        # go to project list
        self.go_projects(project_index)
        if self.config.get('ebs', 'use_ebs') == '1':
            # get velocity rating
            vel_rating = self.projects.data[project_index][
                'logged'] / self.projects.data[project_index]['estimated']
            if vel_rating > 0:
                # save velocity rating to history
                self.velocity_history.append(vel_rating)
                self.save_velocity_history()
        # delete project
        del self.projects.data[project_index]
        self.save_projects()
        self.refresh_projects()

    def edit_project(self, project_index):
        self.current_project_index = project_index
        project = self.projects.data[project_index]
        name = 'project{}'.format(project_index)

        if self.root.has_screen(name):
            self.root.remove_widget(self.root.get_screen(name))

        if self.config.get('ebs', 'use_ebs') == '1':
            if self.config.get('timer', 'use_notepad') == '0':
                view = ProjectViewWithoutNotepad(
                    name=name,
                    project_index=project_index,
                    project_title=project.get('title'),
                    project_estimated=project.get('estimated'),
                    project_logged=project.get('logged'))
            else:
                view = ProjectView(name=name,
                                   project_index=project_index,
                                   project_title=project.get('title'),
                                   project_content=project.get('content'),
                                   project_estimated=project.get('estimated'),
                                   project_logged=project.get('logged'))

        else:
            if self.config.get('timer', 'use_notepad') == '0':
                view = ProjectViewSimpleWithoutNotepad(
                    name=name,
                    project_index=project_index,
                    project_title=project.get('title'))
            else:
                view = ProjectViewSimple(
                    name=name,
                    project_index=project_index,
                    project_title=project.get('title'),
                    project_content=project.get('content'))

        # add widget to root
        self.root.add_widget(view)
        self.transition.direction = 'left'
        self.root.current = view.name
        # update timer logged view
        self.timer.update_logged_string(project.get('logged'))
        # update simulation string
        self.update_simulation_string(project_index)

    def quick_session(self):
        if not self.root.current == '':
            # remove previous quick view screen
            if self.root.has_screen(''):
                self.root.remove_widget(self.root.get_screen(''))
            view = QuickView(project_content='')
            self.root.add_widget(view)
            self.transition.direction = 'left'
            self.root.current = view.name
            self.current_project_index = -1
        self.start_timer()

    def add_project(self):
        self.projects.data.append({
            'title': 'Unnamed Task',
            'content': '',
            'logged': 0,
            'estimated': 1
        })
        project_index = len(self.projects.data) - 1
        self.edit_project(project_index)

    def set_project_content(self, project_index, project_content):
        self.projects.data[project_index]['content'] = project_content
        data = self.projects.data
        self.projects.data = []
        self.projects.data = data
        self.save_projects()
        self.refresh_projects()

    def set_project_title(self, project_index, project_title):
        self.projects.data[project_index]['title'] = project_title
        self.save_projects()
        self.refresh_projects()

    def set_project_estimated(self, project_index, estimated):
        new_estimate = float(estimated)
        # sanity check
        if not new_estimate > 0:
            new_estimate = 1.0
        # save new estimate to project
        self.projects.data[project_index]['estimated'] = new_estimate
        self.save_projects()
        self.refresh_projects()
        # update simulation string
        self.update_simulation_string(project_index)

    def set_project_logged(self, project_index, logged):
        self.projects.data[project_index]['logged'] = float(logged)
        self.save_projects()
        self.refresh_projects()

    def refresh_projects(self):
        data = self.projects.data
        self.projects.data = []
        self.projects.data = data

    def go_projects(self, project_index):
        # stop in- or decrementing time
        Clock.unschedule(self.increment_time)
        Clock.unschedule(self.decrement_time)
        if project_index == -1:
            # quick session view; stop timer
            self.stop_timer()
        else:
            # project view; log work and stop timer
            if self.timer.running_down:
                self.stop_work(project_index)
        # go to project view
        self.transition.direction = 'right'
        self.root.current = 'projects'
        self.current_project_index = -1

    def start_work(self, project_index):
        # start new session if timer completely stopped
        if not self.timer.running_down and not self.timer.running_up:
            # start timer
            self.start_timer()
            # set current project index
            self.current_project_index = project_index
        # or reset and start new session if timer runs up
        if self.timer.running_up:
            # stop counting up
            self.stop_timer()
            # start timer
            self.start_timer()

    def stop_work(self, project_index):
        # only log work when timer running down
        if self.timer.running_down:
            if not project_index == -1:
                # log work
                self.log_work(project_index)
                # save log
                self.refresh_projects()
                self.save_projects()
        # stop timer
        self.stop_timer()

    def log_work(self, project_index):
        # get logged fractional unit: (full session time - remaining time) /  full session time
        full_session_time = float(self.config.get('timer', 'session_length'))
        logged_new = (full_session_time * 60. - (self.timer.minutes * 60. + self.timer.seconds)) / \
                     (full_session_time * 60.)
        logged_total = self.projects.data[project_index]['logged'] + logged_new
        # update logged
        self.set_project_logged(project_index, logged_total)
        # update logged view
        self.timer.update_logged_string(logged_total)
        # update simulation view
        self.update_simulation_string(project_index)

    def start_timer(self):
        # stop in- or decrementing time
        Clock.unschedule(self.increment_time)
        Clock.unschedule(self.decrement_time)
        # start decrementing time
        Clock.schedule_interval(self.decrement_time, 1)
        # store flags that timer is running down
        self.timer.running_up = False
        self.timer.running_down = True
        # play start sound if file found
        if self.timer.start_sound_activated and self.timer.start_sound:
            self.timer.start_sound.play()
        # hide main window if option activated
        if platform == 'win' and self.config.get('system',
                                                 'hide_window') == '1':
            self.root_window.hide()

    def stop_timer(self):
        # stop in- or decrementing time
        Clock.unschedule(self.increment_time)
        Clock.unschedule(self.decrement_time)
        # store flag that timer is not running down or up
        self.timer.running_down = False
        self.timer.running_up = False
        # reinitialize timer
        self.timer.minutes = self.timer.session_length
        self.timer.seconds = 0
        self.timer.update_time_string()

    def decrement_time(self, interval):
        # decrement time of timer
        self.timer.seconds -= 1
        if self.timer.seconds < 0:
            self.timer.minutes -= 1
            self.timer.seconds = 59
        if self.timer.minutes < 0:
            self.timer_alarm()
        self.timer.update_time_string()

    def increment_time(self, interval):
        # increment time of timer
        self.timer.seconds += 1
        if self.timer.seconds > 60:
            self.timer.minutes += 1
            self.timer.seconds = 0
        self.timer.update_time_string()

    def timer_alarm(self):
        # stop decrementing time
        Clock.unschedule(self.decrement_time)
        # set timer to 0:00
        self.timer.minutes = 0
        self.timer.seconds = 0
        # update time string
        self.timer.update_time_string()
        # log work
        if not self.current_project_index == -1:
            self.log_work(self.current_project_index)
        # save log
        self.refresh_projects()
        self.save_projects()
        if platform == 'win' and self.config.get('system',
                                                 'hide_window') == '1':
            # show main window again
            self.root_window.show()
        # log date of completed session to file
        if self.config.get('ebs', 'log_activity') == '1':
            # date and count for this session
            date_today = datetime.datetime.today().strftime('%Y-%m-%d')
            count_today = 1
            # read old file
            if isfile(self.activity_fn):
                with open(self.activity_fn, 'r') as f:
                    # read lines
                    lines = f.readlines()
                    # get date and count
                    date_file, count_file = lines[-1].split()
                    if date_file == date_today:
                        # add count from earlier sessions today
                        count_today += int(count_file)
                        # remove last line with current log for today
                        lines = lines[:-1]
            else:
                lines = []
            # append new last line
            lines.append("%s\t%s\n" % (date_today, str(count_today)))
            # write new file
            with open(self.activity_fn, 'w') as f:
                f.writelines(lines)

        # store flags that timer is not running down but up
        self.timer.running_down = False
        self.timer.running_up = True
        # start incrementing time
        Clock.schedule_interval(self.increment_time, 1)
        # show notification
        if self.timer.notification_activated:
            self.timer.notification_wrapper.notify(
                title="3PM",
                message="Session finished!",
                timeout=self.timer.notification_timeout)
        # play alarm sound if file found
        if self.timer.start_sound_activated and self.timer.alarm_sound:
            self.timer.alarm_sound.play()
        # vibrate on smartphone
        if self.timer.vibration_activated and platform in ['android', 'ios']:
            vibrator.vibrate(2)

    def simulate_completion(self, project_index):
        # only use most recent histories
        number_history = int(self.config.get('ebs', 'number_history'))
        if len(self.velocity_history) > number_history:
            velocity_history = self.velocity_history[-number_history:]
        else:
            velocity_history = self.velocity_history
        # MC simulate completion of project
        sessions_needed = []
        for i in range(0, 100):
            # randomly choose a velocity rating
            vel = random.choice(velocity_history)
            # simulate necessity sessions
            sessions_needed.append(
                vel * self.projects.data[project_index]['estimated'])
        # sort
        sessions_needed = sorted(sessions_needed)
        # pick quartiles
        quartiles = [sessions_needed[i] for i in [24, 49, 74, 99]]
        # calc completion
        logged = float(self.projects.data[project_index]['logged'])
        completion = [logged * 100 / quartiles[i] for i in [0, 1, 2, 3]]
        return quartiles, completion

    def update_simulation_string(self, project_index):
        # simulate completion of project
        quartiles, completion = self.simulate_completion(project_index)
        simulation_string = "%i/%i/%i/%i\n%i%%/%i%%/%i%%/%i%%" % tuple(
            quartiles + completion)
        self.timer.update_simulation_string(simulation_string)

    @property
    def data_dir(self):
        # get settings and data dir
        if self.config.get('system', 'store_in_app') == '1':
            return dirname(realpath(__file__))
        else:
            return self.user_data_dir

    @property
    def projects_fn(self):
        return join(self.data_dir, 'projects.json')

    @property
    def velocity_history_fn(self):
        return join(self.data_dir, 'velocity_history.json')

    @property
    def activity_fn(self):
        return join(self.data_dir, 'daily_activity.txt')
Пример #25
0
class GW2RPC:
    def __init__(self):
        def fetch_registry():

            # First one only for building
            # Only used for debugging without the web based API
            #registry_path = resource_path('./data/registry.json')
            #registry_path = resource_path('../data/registry.json')
            #registry = json.loads(open(registry_path).read())
            #return registry

            url = GW2RPC_BASE_URL + "registry"
            res = requests.get(url)
            if res.status_code != 200:
                log.error("Could not fetch the web registry")
                return None
            return res.json()

        def icon_path():
            try:
                return os.path.join(sys._MEIPASS, "icon.ico")
            except:
                return "icon.ico"

        def fetch_support_invite():
            try:
                return requests.get(GW2RPC_BASE_URL +
                                    "support").json()["support"]
            except:
                return None

        self.rpc = DiscordRPC(GW2RPC_APP_ID)
        self.game = MumbleData()
        self.registry = fetch_registry()
        self.support_invite = fetch_support_invite()
        menu_options = ((_("About"), None, self.about), )
        if self.support_invite:
            menu_options += ((_("Join support server"), None,
                              self.join_guild), )
        self.systray = SysTrayIcon(icon_path(),
                                   _("Guild Wars 2 with Discord"),
                                   menu_options,
                                   on_quit=self.shutdown)
        self.systray.start()
        self.process = None
        self.last_map_info = None
        self.last_continent_info = None
        self.last_boss = None
        self.boss_timestamp = None
        self.no_pois = set()
        self.check_for_updates()

    def shutdown(self, _=None):
        os._exit(0)  # Nuclear option

    def about(self, _):
        message = (
            "Version: {}\n\nhttps://gw2rpc.info\n\nBy Maselkov & "
            "N1tR0\nIcons by Zebban\nWebsite by Penemue\nTranslations by Seshu (de), TheRaytheone (es), z0n3g (fr)"
            .format(VERSION))
        threading.Thread(target=create_msgbox, args=[message]).start()

    def join_guild(self, _):
        try:
            webbrowser.open(self.support_invite)
        except webbrowser.Error:
            pass

    def check_for_updates(self):
        def get_build():
            url = GW2RPC_BASE_URL + "build"
            r = requests.get(url)
            try:
                return r.json()["build"]
            except:
                return None

        build = get_build()
        if not build:
            log.error("Could not retrieve build!")
            create_msgbox(
                _("Could not check for updates - check your connection!"))
            return
        if build > VERSION:
            log.info("New version found! Current: {} New: {}".format(
                VERSION, build))
            res = create_msgbox(_(
                "There is a new update for GW2 Rich Presence available. "
                "Would you like to be taken to the download page now?"),
                                code=68)
            if res == 6:
                webbrowser.open("https://gw2rpc.info/")

    def get_map_asset(self, map_info):
        map_id = map_info["id"]
        map_name = map_info["name"]
        region = str(map_info.get("region_id", "thanks_anet"))

        position = self.game.get_position()
        #print("{} {}".format(position.x, position.y))
        #print("{} {}".format(map_id, map_name))
        #print("{} {}".format(position.x, position.y))

        if self.registry:
            if region == "26":  #  Fractals of the Mists
                image = "fotm"
                for fractal in self.registry["fractals"]:
                    state = self.find_fractal_boss(map_id, fractal, position)
                    if state:
                        break

                    if fractal["id"] == map_id:
                        state = _("in ") + _("fractal") + ": " + _(
                            fractal["name"])
                        break
                else:
                    if not state:
                        state = _("in ") + _("Fractals of the Mists")
                name = "Fractals of the Mists"
            else:
                if map_name in self.registry["special"]:
                    image = self.registry["special"][map_name]
                elif map_id in self.registry["valid"]:
                    image = map_id
                elif region in self.registry["regions"]:
                    image = self.registry["regions"][region]
                else:
                    image = "default"
                name = map_name
                state = _("in ") + name
        else:
            # Fallback for api
            special = {
                "1068": "gh_hollow",
                "1101": "gh_hollow",
                "1107": "gh_hollow",
                "1108": "gh_hollow",
                "1121": "gh_hollow",
                "1069": "gh_precipice",
                "1076": "gh_precipice",
                "1071": "gh_precipice",
                "1104": "gh_precipice",
                "1124": "gh_precipice",
                "882": "wintersday_snowball",
                "877": "wintersday_snowball",
                "1155": "1155",
                "1214": "gh_haven",
                "1215": "gh_haven",
                "1232": "gh_haven",
                "1224": "gh_haven",
                "1243": "gh_haven",
                "1250": "gh_haven"
            }.get(map_info["id"])
            if special:
                return special
            if map_info["type"] == "Public":
                image = map_id
            else:
                valid_ids = [1062, 1149, 1156, 38, 1264]
                if map_id in valid_ids:
                    image = map_id
                else:
                    image = "default"
            name = map_name
            state = _("in ") + name
        return state, {"large_image": str(image), "large_text": _(name)}

    def get_raid_assets(self, map_info):
        def readable_id(_id):
            _id = _id.split("_")
            dont_capitalize = ("of", "the", "in")
            return " ".join([
                x.capitalize() if x not in dont_capitalize else x for x in _id
            ])

        boss = self.find_closest_boss(map_info)
        if not boss:
            self.boss_timestamp = None
            return self.get_map_asset(map_info)
        if boss["type"] == "boss":
            state = _("fighting ")
        else:
            state = _("completing ")
        name = _(readable_id(boss["id"]))
        state += name
        if self.last_boss != boss["id"]:
            self.boss_timestamp = int(time.time())
        self.last_boss = boss["id"]
        return state, {
            "large_image": boss["id"],
            "large_text": name + " - {}".format(map_info["name"])
        }

    def get_activity(self):
        def get_region():
            world = api.world
            if world:
                for k, v in worlds.items():
                    if world in v:
                        return " [{}]".format(k)
            return ""

        def get_closest_poi(map_info, continent_info):
            #region = map_info.get("region_name")
            region = map_info.get("region_id")
            if config.disable_pois:
                return None
            if config.disable_pois_in_wvw and region == 7:  #TODO change to region_Id
                return None
            return self.find_closest_point(map_info, continent_info)

        data = self.game.get_mumble_data()
        if not data:
            return None
        map_id = data["map_id"]
        is_commander = data["commander"]
        try:
            if self.last_map_info and map_id == self.last_map_info["id"]:
                map_info = self.last_map_info
            else:
                map_info = api.get_map_info(map_id)
                self.last_map_info = map_info
            character = Character(data)
        except APIError:
            log.exception("API Error!")
            self.last_map_info = None
            return None
        state, map_asset = self.get_map_asset(map_info)
        tag = character.guild_tag if config.display_tag else ""
        try:
            if map_id in self.no_pois or "continent_id" not in map_info:
                raise APIError(404)
            if (self.last_continent_info
                    and map_id == self.last_continent_info["id"]):
                continent_info = self.last_continent_info
            else:
                continent_info = api.get_continent_info(map_info)
                self.last_continent_info = continent_info
        except APIError:
            self.last_continent_info = None
            self.no_pois.add(map_id)
        details = character.name + tag
        timestamp = self.game.last_timestamp
        if self.registry and str(map_id) in self.registry.get("raids", {}):
            state, map_asset = self.get_raid_assets(map_info)
            timestamp = self.boss_timestamp or self.game.last_timestamp
        else:
            self.last_boss = None
            if self.last_continent_info:
                point = get_closest_poi(map_info, continent_info)
                if point:
                    map_asset["large_text"] += _(" near ") + point["name"]
        map_asset["large_text"] += get_region()

        if not config.hide_commander_tag and is_commander:
            small_image = "commander_tag"
            details = "{}: {}".format(_("Commander"), details)
        else:
            small_image = character.profession_icon
        small_text = "{} {} {}".format(_(character.race),
                                       _(character.profession), tag)

        activity = {
            "state": _(state),
            "details": details,
            "timestamps": {
                'start': timestamp
            },
            "assets": {
                **map_asset, "small_image": small_image,
                "small_text": small_text
            }
        }
        return activity

    def in_character_selection(self):
        activity = {
            "state": _("in character selection"),
            "assets": {
                "large_image":
                "default",
                "large_text":
                _("Character Selection"),
                "small_image":
                "gw2rpclogo",
                "small_text":
                "GW2RPC Version {}\nhttps://gw2rpc.info".format(VERSION)
            }
        }
        return activity

    def convert_mumble_coordinates(self, map_info, position):
        crect = map_info["continent_rect"]
        mrect = map_info["map_rect"]
        x = crect[0][0] + (position.x - mrect[0][0]) / 24
        y = crect[0][1] + (mrect[1][1] - position.y) / 24
        return x, y

    def find_closest_point(self, map_info, continent_info):
        position = self.game.get_position()
        x_coord, y_coord = self.convert_mumble_coordinates(map_info, position)
        lowest_distance = float("inf")
        point = None
        for item in continent_info["points_of_interest"].values():
            if "name" not in item:
                continue
            distance = (item["coord"][0] - x_coord)**2 + (item["coord"][1] -
                                                          y_coord)**2
            if distance < lowest_distance:
                lowest_distance = distance
                point = item
        return point

    def find_closest_boss(self, map_info):
        position = self.game.get_position()
        x_coord, y_coord = self.convert_mumble_coordinates(map_info, position)
        closest = None
        for boss in self.registry["raids"][str(map_info["id"])]:
            distance = math.sqrt((boss["coord"][0] - x_coord)**2 +
                                 (boss["coord"][1] - y_coord)**2)
            if "radius" in boss and distance < boss["radius"]:
                if "height" in boss:
                    if position.z < boss["height"]:
                        closest = boss
                else:
                    closest = boss
        return closest

    def find_fractal_boss(self, map_id, fractal, position):
        state = None
        if map_id in [1177, 1205, 1384]:
            if fractal["id"] == map_id:
                for boss in fractal["bosses"]:
                    distance = math.sqrt((boss["coord"][0] - position.x)**2 +
                                         (boss["coord"][1] - position.y)**2)

                    if distance <= boss["radius"]:
                        state = _("fighting ") + _(
                            boss["name"]) + " " + _("in ") + _(fractal["name"])
                        return state
                    else:
                        state = _("in ") + _("fractal") + ": " + _(
                            fractal["name"])
        else:
            return None
        return state

    def main_loop(self):
        def update_gw2_process():
            shutdown = False
            if self.process:
                if self.process.is_running():
                    return
                else:
                    if config.close_with_gw2:
                        shutdown = True
            try:
                for process in psutil.process_iter(attrs=['name']):
                    name = process.info['name']
                    if name in ("Gw2-64.exe", "Gw2.exe"):
                        self.process = process
                        return
            except psutil.NoSuchProcess:
                log.debug(
                    "A process exited while iterating over the process list.")
                pass

            if shutdown:
                self.shutdown()
            self.process = None
            raise GameNotRunningError

        def start_rpc():
            while True:
                try:
                    self.rpc.start()
                    break
                except (FileNotFoundError, PermissionError) as e:
                    time.sleep(10)

        try:
            while True:
                try:
                    update_gw2_process()
                    if not self.game.memfile:
                        self.game.create_map()
                    if not self.rpc.running:
                        start_rpc()
                        log.debug("starting self.rpc")
                    try:
                        data = self.get_activity()
                    except requests.exceptions.ConnectionError:
                        raise GameNotRunningError
                    if not data:
                        data = self.in_character_selection()
                    log.debug(data)
                    try:
                        self.rpc.send_rich_presence(data, self.process.pid)
                    except BrokenPipeError:
                        raise GameNotRunningError  # To start a new connection
                except GameNotRunningError:
                    #  TODO
                    self.game.close_map()
                    if self.rpc.running:
                        self.rpc.close()
                        log.debug("Killing RPC")
                time.sleep(15)
        except Exception as e:
            log.critical("GW2RPC has crashed", exc_info=e)
            create_msgbox(
                "GW2 Rich Presence has crashed.\nPlease check your "
                "log file and report this to the author!",
                code=16)
            self.shutdown()
Пример #26
0
def systray_with_infisystray():
    from infi.systray import SysTrayIcon
    menu_options = (("Say Hello", None, say_hello),)
    systray = SysTrayIcon("icon.ico", "Example tray icon", menu_options)
    systray.start()
Пример #27
0
from __future__ import print_function
from infi.systray import SysTrayIcon
import os
import ctypes

icon_path = os.path.join(os.path.dirname(__file__), "test.ico")
shutdown_called = False
def on_quit(systray):
    print("Bye")
def do_example(systray):
    print("Example")
def on_about(systray):
    ctypes.windll.user32.MessageBoxW(None, u"This is a test of infi.systray", u"About", 0)

menu_options = (("Example", None, do_example),
                ("About", None, on_about))
systray = SysTrayIcon(icon_path, "Systray Test", menu_options, on_quit)
systray.start()
Пример #28
0
class DesktopUtils:
    VERSION = "1.0"
    
    def __init__(self):
        self.widgets = {}
        self.running_gui = False
        self.refresh_thread = threading.Thread(target=self.refresh_widgets)

        menu = (
            ("Open", "./img/icon.ico", self._open_gui),
            ("Exit", "./img/x.ico", self.__shutdown),
        )

        self.stray = SysTrayIcon("./img/icon.ico", "DeskopUtils", menu_options=menu)

    def __shutdown(self, *_):
        def stop():
            DestroyWindow(self.stray._hwnd)

            for widget in self.widgets:
                instance = self.widgets[widget]["instance"]

                try:
                    instance.root.destroy()
                except RuntimeError:
                    pass

        t = threading.Thread(target=stop)
        t.start()

        self.refresh_thread.join()
        sys.exit(0)

    def _run_stray(self):
        self.stray.start()
    
    def _open_gui(self, _):
        if self.running_gui:
            return

        self.running_gui = True

        root = tk.Tk()
        root.geometry("500x500")
        root.title("DesktopUtils V" + self.VERSION)

        def close_win():
            root.destroy()
            self.running_gui = False
            self._run_stray()

        root.protocol("WM_DELETE_WINDOW", close_win)

        root.mainloop()
    
    def create_root(self, widget) -> tk.Tk:
        root = tk.Tk()
        root.overrideredirect(True)
        root.wm_resizable(*widget.RESIZE)
        root.geometry(f"{widget.SIZE[0]}x{widget.SIZE[1]}+{widget.START_POS[0]}+{widget.START_POS[1]}")

        return root

    def add_widget(self, info, widget):
        generalInfo = info.copy()
        generalInfo.pop("NAME")

        self.widgets[info["NAME"]] = {
            "information": generalInfo,
            "plugin": widget
        }

    def refresh_widgets(self):
        time.sleep(10)

        while True:
            for widget in self.widgets:
                if self.widgets[widget]["instance"].REFRESH:
                    self.widgets[widget]["instance"].refresh()

            time.sleep(20)

    def _create_bar(self, root, w_):
        top_bar = tk.Frame(root, bg=w_.BAR_COLOR, height=25, width=200)

        xImage = tk.PhotoImage(file="./img/x.png")
        x = tk.Button(top_bar, image=xImage, borderwidth=0, highlightthickness=0, command=w_.quit)
        x.photo = xImage

        x.place(x=180, y=2)

        top_bar.bind("<ButtonRelease-1>", getattr(w_, "_Widget__button_release"))
        top_bar.bind("<ButtonPress-1>", getattr(w_, "_Widget__button_press"))
        top_bar.bind("<B1-Motion>", getattr(w_, "_Widget__move_window"))
        top_bar.pack(side=tk.TOP, anchor=tk.E)

        return top_bar

    def run(self):
        pluginPaths = next(os.walk("./plugins/"))[1]
        for folderName in pluginPaths:
            path = "./plugins/" + folderName

            try:
                with open(path + "/info.meda", "r") as fh:
                    infoMeDa = fh.readlines()
            except FileNotFoundError:
                print("Could not load " + folderName + ". info.meda is missing!")
                continue

            pluginInfo = {
                "NAME": None,
                "AUTHOR": None,
                "DESCRIPTION": None,
                "VERSION": None,
                "MAIN": None,
                "CLASS": None
            }

            for line in infoMeDa:
                key, value = line.split("=")

                if key in pluginInfo:
                    pluginInfo[key] = value.replace("\n", "").replace(" ", "_")

            assert pluginInfo["MAIN"] is not None

            path = path[2:].replace("/", ".")
            main_filename = pluginInfo["MAIN"].replace(".py", "")
            main_ = importlib.import_module(".." + main_filename, path)

            class_ = getattr(main_, pluginInfo["CLASS"])

            self.add_widget(pluginInfo, class_)

        self._run_stray()

        self.refresh_thread.start()

        for widget in self.widgets:
            root = self.create_root(self.widgets[widget]["plugin"])
            w = self.widgets[widget]["plugin"](root)
            self.widgets[widget]["instance"] = w

            self._create_bar(root, w)

            w.run()
Пример #29
0
class SysTrayPlugin(plugins.SimplePlugin):
    '''
    CherryPy plugin that creates a system tray icon for Windows.

    Because SysTrayIcon always fires off on_quit, we can't have on_quit
        execute cherrypy.engine.exit() if the exit command is what triggered
        SysTrayIcon to close. So conditions are set to only fire on_quit when
        the quit_method == 'menu'.

    This way, when the menu option is called, it destroys SysTrayIcon then
        closes cherrypy. Cherrypy will try to close SysTrayIcon by calling
        stop(), so stop() gets reassigned to None.

    If the app is closed by cherrypy (whether catching a kb interrupt or the GUI
        shutdown button), cherrypy stops the plugin by calling stop(). Stop()
        reassigns SysTrayIcon._on_quit to None and calls SysTrayIcon.shutdown().
        SysTrayIcon is then destroyed (twice for reasons I can't figure out),
        then cherrypy finishes up the engine.stop() and engine.exit().

    The chain is as such:

    Trigger == systray menu 'Quit':
    SysTrayIcon._destroy() >
    SysTrayIcon._on_quit() > set SysTrayPlugin.quit_method = 'menu'
    cherrypy.engine.exit() >
    SysTrayPlugin.stop()   > does nothing
    sys.exit()

    Trigger == KBInterrupt or GUI Shutdown:
    cherrypy.engine.stop() >
    SysTrayPlugin.stop()   > disable SysTrayIcon._on_quit()
    SysTrayIcon.shutdown() >
    SysTrayIcon._destroy() >
    SysTrayIcon._destroy() >
    cherrypy.engine.exit() >
    sys.exit()

    '''

    def __init__(self, bus):
        plugins.SimplePlugin.__init__(self, bus)
        menu_options = (('Open Browser', None, self.open),)
        self.systray = SysTrayIcon('core/favicon.ico', 'Watcher',
                                   menu_options, on_quit=self.on_quit)
        self.quit_method = None
        return

    def start(self):
        self.systray.start()
        return

    def stop(self):
        if self.quit_method == 'menu':
            return
        else:
            self.systray._on_quit = None
            self.systray.shutdown()
            return

    def on_quit(self, systray):
        self.quit_method = 'menu'
        cherrypy.engine.exit()
        sys.exit(0)

    # sys tray functions:
    def open(self, systray):
        webbrowser.open(u'http://{}:{}{}'.format(
            core.SERVER_ADDRESS, core.SERVER_PORT, core.URL_BASE))
        return
Пример #30
0
            if Var_name.strip()==variable.strip():
                return Var_path
    return ""





if __name__ == "__main__":
    menu_options = (("Buona notte", None, say_hello),) # creazione menu icona
    systray = SysTrayIcon(".\icon.ico", "Polito Materiale", menu_options)
    # creazione icona applicazione
    checkConfig()
    print()
    # Creo la sessione.
    systray.start()

    printy("PoliTo Materiale - v 1.2.0\n", "b")
    sess = PolitoWeb()

    # Imposto la cartella di download di default
    
    home = os.path.expanduser('~')
    if sys.platform.startswith('win'):
        path_materiale=getVar("Percorso-Materiale")
        sess.set_dl_folder(path_materiale)
    else:
        sess.set_dl_folder(home + "/polito-materiale")

    # Togliere il commento dalla riga seguente e modificarlo nel caso si volesse settare
    # una cartella per il download diversa da quella di default
Пример #31
0
class GW2RPC:
    def __init__(self):
        def fetch_registry():
            url = GW2RPC_BASE_URL + "registry"
            res = requests.get(url)
            if res.status_code != 200:
                log.error("Could not fetch the web registry")
                return None
            return res.json()

        def icon_path():
            try:
                return os.path.join(sys._MEIPASS, "icon.ico")
            except:
                return "icon.ico"

        def fetch_support_invite():
            try:
                return requests.get(GW2RPC_BASE_URL +
                                    "support").json()["support"]
            except:
                return None

        self.rpc = DiscordRPC(GW2RPC_APP_ID)
        self.game = MumbleData()
        self.registry = fetch_registry()
        self.support_invite = fetch_support_invite()
        menu_options = (("About", None, self.about), )
        if self.support_invite:
            menu_options += (("Join support server", None, self.join_guild), )
        self.systray = SysTrayIcon(icon_path(),
                                   "Guild Wars 2 with Discord",
                                   menu_options,
                                   on_quit=self.shutdown)
        self.systray.start()
        self.process = None
        self.check_for_updates()

    def shutdown(self, _=None):
        os._exit(0)  # Nuclear option

    def about(self, _):
        message = (
            "Version: {}\n\nhttps://gw2rpc.info\n\nBy Maselkov & "
            "N1TR0\nIcons by Zebban\nWebsite by Penemue".format(VERSION))
        threading.Thread(target=create_msgbox, args=[message]).start()

    def join_guild(self, _):
        try:
            webbrowser.open(self.support_invite)
        except:
            pass

    def check_for_updates(self):
        def get_build():
            url = GW2RPC_BASE_URL + "build"
            r = requests.get(url)
            try:
                return r.json()["build"]
            except:
                return None

        build = get_build()
        if not build:
            log.error("Could not retrieve build!")
            create_msgbox(
                "Could not check for updates - check your connection!")
            return
        if build > VERSION:
            log.info("New version found! Current: {} New: {}".format(
                VERSION, build))
            res = create_msgbox(
                "There is a new update for GW2 Rich Presence available. "
                "Would you like to be taken to the download page now?",
                code=68)
            if res == 6:
                webbrowser.open("https://gw2rpc.info/")

    def update_gw2_process(self):
        if self.process:
            if self.process.is_running():
                return
            else:
                if config.close_with_gw2:
                    self.shutdown()
        for pid in psutil.pids():
            try:
                p = psutil.Process(pid)
                pname = p.name()
                if pname == "Gw2-64.exe" or pname == "Gw2.exe":
                    self.process = p
                    return
            except:
                pass
        self.process = None

    def get_map_asset(self, map_info):
        def get_region():
            world = api.world
            if world:
                for k, v in worlds.items():
                    if world in v:
                        return " [{}]".format(k)
            return ""

        map_id = map_info["id"]
        map_name = map_info["name"]
        region = map_info.get("region_name", "thanks_anet")
        if self.registry:
            if map_name == "Fractals of the Mists":
                for fractal in self.registry["fractals"]:
                    if fractal["id"] == map_id:
                        state = fractal["name"] + " fractal"
                        image = "fotm"
                        break
                else:
                    image = "fotm"
                    state = "Fractals of the Mists"
                name = "Fractals of the Mists"
            else:
                if map_name in self.registry["special"]:
                    image = self.registry["special"][map_name]
                elif map_id in self.registry["valid"]:
                    image = map_id
                elif region in self.registry["regions"]:
                    image = self.registry["regions"][region]
                else:
                    image = "default"
                name = map_name
                state = name
        else:
            special = {
                "Fractals of the Mists": "fotm",
                "Windswept Haven": "gh_haven",
                "Gilded Hollow": "gh_hollow",
                "Lost Precipice": "gh_precipice"
            }.get(map_info["name"])
            if special:
                return special
            if map_info["type"] == "Public":
                image = map_id
            else:
                valid_ids = [1062, 1149, 1156, 38, 1264]
                if map_id in valid_ids:
                    image = map_id
                else:
                    image = "default"
            name = map_name
            state = name
        return "in " + state, {
            "large_image": str(image),
            "large_text": name + get_region()
        }

    def get_activity(self):
        data = self.game.get_mumble_data()
        if not data:
            return None
        current_time = time.time()
        map_id = data["map_id"]
        try:
            map_info = api.get_map_info(map_id)
            character = Character(data)
        except APIError:
            log.exception("API Error!")
            return None
        state, map_asset = self.get_map_asset(map_info)
        if config.display_tag:
            tag = character.guild_tag
        else:
            tag = ""
        activiy = {
            "state": state,
            "details": character.name + tag,
            "timestamps": {
                'start': int(current_time)
            },
            "assets": {
                **map_asset, "small_image": character.profession_icon,
                "small_text": "{0.race} {0.profession}".format(character)
            }
        }
        return activiy

    def in_character_selection(self):
        current_time = time.time()
        activity = {
            "state": "in character selection",
            "timestamps": {
                'start': int(current_time)
            },
            "assets": {
                "large_image":
                "default",
                "large_text":
                "Character Selection",
                "small_image":
                "gw2rpclogo",
                "small_text":
                "GW2RPC Version {}\nhttps://gw2rpc.info".format(VERSION)
            }
        }
        return activity

    def main_loop(self):
        def gw2_running():
            if not self.process:
                return False
            if not self.process.is_running():
                return False
            return True

        def start_rpc():
            while True:
                try:
                    self.rpc.start()
                    break
                except (FileNotFoundError, PermissionError) as e:
                    time.sleep(10)

        try:
            while True:
                try:
                    self.update_gw2_process()
                    if not gw2_running():
                        raise GameNotRunningError
                    if not self.rpc.running:
                        start_rpc()
                        log.debug("starting self.rpc")
                    data = self.get_activity()
                    if not data:
                        data = self.in_character_selection()
                    log.debug(data)
                    try:
                        self.rpc.send_rich_presence(data, self.process.pid)
                    except BrokenPipeError:
                        raise GameNotRunningError  # To start a new connection
                except GameNotRunningError:
                    #  TODO
                    if self.rpc.running:
                        self.rpc.last_payload = {}
                        self.rpc.last_pid = None
                        self.rpc.last_update = time.time()
                        self.game.last_character = None
                        self.game.last_map_id = None
                        self.rpc.close()
                        log.debug("Killing RPC")
                except DataUnchangedError:
                    pass
                time.sleep(config.update_frequency)
        except Exception as e:
            log.critical("GW2RPC has crashed", exc_info=e)
            create_msgbox(
                "GW2 Rich Presence has crashed.\nPlease check your "
                "log file and report this to the author!",
                code=16)
            self.shutdown()