Esempio n. 1
0
 def __init__(self):
     sg.theme('GreenTan')
     self.data = uData.gui_setting()
     self.layout = self.create_layout()
     self.window = sg.Window(f'kirafan-bot  v{uData.setting["version"]}', self.layout, finalize=True)
     self.update_tab_selected(self.find_tab_by_name(self.data['questList']['quest_selector']).id)
     self.update_tabs_bind()
     self.update_adb_bind()
     self.battle_job = self.visit_room_job = self.cork_shop_job = Job()
     self.hotkey = Hotkey('s', mode='gui', window=self.window)
     if self.data['adb']['use']:
         self.hotkey.remove_all_hotkey()
     logger.propagate = False
     logger.addHandler(GUI_Handler(self.window, '_log_box_', self.blocked))
     self._open_re = re.compile(r'^_(\d+|button|update|tab_group|adb|timer|log_level|sleep)_.*$')
Esempio n. 2
0
    def __init__(self, g_pool, annotation_definitions=None):
        super().__init__(g_pool)
        self.menu = None
        self._annotation_list_menu = None

        if annotation_definitions is None:
            annotation_definitions = [[
                "My annotation",
                Hotkey.ANNOTATION_EVENT_DEFAULT_HOTKEY()
            ]]
        self._initial_annotation_definitions = annotation_definitions
        self._definition_to_buttons = {}

        self._new_annotation_label = "new annotation label"
        self._new_annotation_hotkey = Hotkey.ANNOTATION_EVENT_DEFAULT_HOTKEY()
Esempio n. 3
0
class HotkeyThread(QThread):

    fire = Signal()

    def __init__(self):
        QThread.__init__(self)
        from hotkey import Hotkey
        self.hotkey = Hotkey(self.handle_hotkey)

    def handle_hotkey(self):
        self.fire.emit()

    def stop(self):
        self.hotkey.stop()

    def run(self):
        self.hotkey.start()
Esempio n. 4
0
 def init_ui(self):
     self.add_menu()
     self.menu.label = self.pretty_class_name
     self.add_button = ui.Thumb(
         "add_surface",
         setter=self.on_add_surface_click,
         getter=lambda: False,
         label="A",
         hotkey=Hotkey.SURFACE_TRACKER_ADD_SURFACE_CAPTURE_AND_PLAYER_HOTKEY(),
     )
     self.g_pool.quickbar.append(self.add_button)
     self._update_ui()
Esempio n. 5
0
 def __init__(self):
     hCheck = win32gui.FindWindow(None, 'kcon')
     if hCheck > 0:
         echo('Program has been running')
     else:
         self.root = Tk()
         self.root.title('kcon')
         hwnd = self.root.winfo_id()
         self.box = self.TaskList(self.root)
         self.edit = self.EditLine(self.root, self.box)
         self.root.bind_all('<Control-Key-a>', self.onCtrlA)
         self.root.bind_all("<Key>", self.onPress)
         self.root.bind_all('<Return>', self.onReturn)
         self.hotkey = Hotkey(self.root)
         self.root.mainloop()
Esempio n. 6
0
def main():
    if uData.setting['mode'].lower() == 'gui':
        kirafanbot_GUI().open()
    elif uData.setting['mode'].lower() == 'hotkey':
        try:
            hotkey = Hotkey('rslmptcoxkvei')
            logger.info("kirafan-bot: hotkey setting finish...")
            logger.info(f'kirafan-bot: region = {list(kirafan.region)}')
            logger.info(
                f'kirafan-bot: adb use = {uData.setting["adb"]["use"]}')
            logger.info(
                f'kirafan-bot: quest setting = \x1b[41m{kirafan.quest_name}\x1b[0m'
            )
            check_basic_information()
            logger.info('please press \'f3\' button to exit...')
            hotkey.wait('f3')
            hotkey.safe_exit()
        except KeyboardInterrupt:
            hotkey.safe_exit()
Esempio n. 7
0
def main():
    all_bots = ", ".join(get_all_bots())
    parser = argparse.ArgumentParser()
    parser.add_argument("-b", "--bot", help=f"Available bots: {all_bots}")
    args = parser.parse_args()

    h = Hotkey()
    if args.bot:
        try:
            bot_module = __import__(f"bots.{args.bot}",
                                    fromlist=('PressKey', ))
            bot = bot_module.PressKey()
            h.load_bot(bot)
        except ModuleNotFoundError as e:
            logger.error(e)

    h.run()
Esempio n. 8
0
class kirafanbot_GUI():
    def __init__(self):
        sg.theme('GreenTan')
        self.data = uData.gui_setting()
        self.layout = self.create_layout()
        self.window = sg.Window(f'kirafan-bot  v{uData.setting["version"]}', self.layout, finalize=True)
        self.update_tab_selected(self.find_tab_by_name(self.data['questList']['quest_selector']).id)
        self.update_tabs_bind()
        self.update_adb_bind()
        self.battle_job = self.visit_room_job = self.cork_shop_job = Job()
        self.hotkey = Hotkey('s', mode='gui', window=self.window)
        if self.data['adb']['use']:
            self.hotkey.remove_all_hotkey()
        logger.propagate = False
        logger.addHandler(GUI_Handler(self.window, '_log_box_', self.blocked))
        self._open_re = re.compile(r'^_(\d+|button|update|tab_group|adb|timer|log_level|sleep)_.*$')

    def open(self):
        _map = {
            'tab': lambda event, values: self.handle_tab_event(self.find_tab_by_key(event), event, values),
            'tab_group': lambda event, values: self.handle_tab_group_event(values[event]),
            'sleep': lambda event, values: self.handle_sleep_event(event, values[event]),
            'timer': lambda event, values: self.handle_timer_event(event, values[event]),
            'adb': lambda event, values: self.handle_adb_event(event, values[event]),
            'button': lambda event, values: self.handle_button_event(event),
            'log_level': lambda event, values: (self.data.update({'loglevel': values[event]}), logger.setLevel(values[event])),
            'update': lambda event, values: self.handle_update_event(event, values[event])
        }
        while True:
            event, values = self.window.read()
            if event in (sg.WIN_CLOSED, '_button_Exit_'):
                self.__save()
                self.stop_all_safe()
                break

            # print(f'event={event}, '
            #       f'(value, type)={(values[event], type(values[event])) if event in values else ("none", "none_type")}')
            matchresult = self._open_re.match(event)
            if matchresult:
                _cls = 'tab' if matchresult[1].isdigit() else matchresult[1]
                _map[_cls](event, values)
        self.window.close()

    def find_tabskey_by_id(self, id: str) -> Optional[str]:
        for k, v in self.tabs.items():
            if v.id == id:
                return k
        return None

    def find_tab_by_key(self, key: str) -> Optional[Tab_Frame]:
        try:
            id = key[key.index('_')+1:key.index('_', 1)]
            return self.tabs[self.find_tabskey_by_id(id)] if id.isdigit() else None
        except ValueError:
            return None

    def find_tab_by_name(self, name: str) -> Optional[Tab_Frame]:
        for tab in self.tabs.values():
            if tab.name == name:
                return tab
        return None

    def update_tab_selected(self, tab_id: str):
        self.window[tab_id].select()

    def update_tabs_bind(self):
        for _, tab in self.tabs.items():
            tab.update_all_bind(self.window)

    def update_adb_bind(self):
        if self.data['adb']['use']:
            self.window['_adb_serial_'].bind('<Button-1>', '')
        else:
            self.window['_adb_serial_'].unbind('<Button-1>')

    def check_configure_and_status(self):
        if (self.data['questList']['quest_selector'] != self.tabs[self.window['_tab_group_'].get()].name or
           self.tabs[self.window['_tab_group_'].get()].is_modified() or
           not data_compare(self.__original_timer, self.data['set_timer']) or
           not data_compare(self.__original_sleep, self.data['sleep'])):
            self.data['questList']['quest_selector'] = self.tabs[self.window['_tab_group_'].get()].name
            self.bt_reset_event()
        logger.info(f'kirafan-bot: region = {list(kirafan.region)}')
        logger.debug(f'kirafan-bot: adb use = \x1b[35m{uData.setting["adb"]["use"]}\x1b[0m')
        logger.debug(f'kirafan-bot: quest setting = \x1b[41m{kirafan.quest_name}\x1b[0m')

    def handle_tab_event(self, tab: Tab_Frame, event: str, values: Optional[Dict]):
        if event.endswith('_rename_'):
            old_name = tab.name
            exclude = [t.name for _, t in self.tabs.items()] + ['quest_selector']
            # magic: tab key will be modified by window[tab_key].Update() after executing tab.hide()
            #        therefore, window['_tab_group_'].get() will confused.
            o_key = self.window['_tab_group_'].get()
            new_name = tab.rename_title(self.window, exclude)
            n_key = self.window['_tab_group_'].get()
            # update key value, avoid tab key being modified.
            if n_key != o_key:
                self.tabs[n_key] = self.tabs.pop(o_key)
            if new_name:
                self.data['questList'] = {new_name if k == old_name else k: v for k, v in self.data['questList'].items()}
                if self.data['questList']['quest_selector'] == old_name:
                    self.data['questList']['quest_selector'] = new_name
        elif event.endswith('_delete_'):
            return_button = sg.popup_ok_cancel(f"Are you sure you want to delete '{tab.name}' tab?", title='Confirm delete')
            if return_button == 'OK':
                del self.data['questList'][tab.name]
                del self.tabs[self.find_tabskey_by_id(tab.id)]
                self.window['_tab_group_'].Widget.hide(int(tab.id))
                if self.data['questList']['quest_selector'] == tab.name:
                    self.data['questList']['quest_selector'] = self.tabs[self.window['_tab_group_'].get()].name if self.window['_tab_group_'].get() else ''  # noqa: E501
                if self.window['_tab_group_'].get() == self.find_tab_by_name('+').id:
                    k = self.window['_tab_group_'].get()
                    self.window[k].update(disabled=True)
                    self.window[k].update(disabled=False)
        else:
            tab.handle(self.window, event, values)

    def handle_tab_group_event(self, tab_id: Optional[str]):
        def __gen_tab_name():
            i = 1
            while True:
                n = f'new tab {i}'
                if n not in [t.name for _, t in self.tabs.items()]:
                    return n
                i += 1
        self.window['_tab_group_'].set_focus()
        if tab_id is not None and int(tab_id) == int(self.next_id) - 1:
            tab = self.find_tab_by_name('+')
            self.data['questList']['+'] = tab.quest
            new_name = __gen_tab_name()
            self.window[f'_{tab.id}_{tab.name}_title_'].update(new_name)
            self.handle_tab_event(tab, '_rename_', None)
            self.window['_tab_group_'].add_tab(self.__tab_plus()[0])

    def handle_sleep_event(self, key: str, value: str):
        key = key[7:-1]
        if value.replace('.', '', 1).isdigit():
            self.data['sleep'][key] = float(value)
        elif value == '':
            self.data['sleep'][key] = 0

    def handle_timer_event(self, key: str, value):
        if key == '_timer_use_':
            for tk in ['_timer_hour_start_', '_timer_min_start_', '_timer_sec_start_',
                       '_timer_hour_end_', '_timer_min_end_', '_timer_sec_end_']:
                self.window[tk].update(disabled=(not value))
            self.data['set_timer']['use'] = value
        elif key == '_timer_countdown_':
            self.window[key].update(value)
        elif key in ['_timer_hour_start_', '_timer_min_start_', '_timer_sec_start_',
                     '_timer_hour_end_', '_timer_min_end_', '_timer_sec_end_']:
            self.data['set_timer']['pause_range'] = '{}:{}:{}-{}:{}:{}'.format(
                self.window['_timer_hour_start_'].get(),
                self.window['_timer_min_start_'].get(),
                self.window['_timer_sec_start_'].get(),
                self.window['_timer_hour_end_'].get(),
                self.window['_timer_min_end_'].get(),
                self.window['_timer_sec_end_'].get()
            )

    def handle_adb_event(self, key: str, value):
        key = key[5:-1]
        if key == 'use':
            if value:
                self.hotkey.remove_all_hotkey()
            else:
                self.hotkey.add_hotkey()
            self.data['adb'][key] = value
            self.window['_adb_serial_'].Widget.config(readonlybackground=('white' if value else 'gray'))
            self.window['_adb_path_'].Widget.config(readonlybackground=('white' if value else 'gray'))
            self.window['_adb_browse_'].update(disabled=(not value))
            self.window['_button_Game region_'].update(disabled=value)
            self.update_tips_information()
            self.update_adb_bind()
            uData.adb_mode_switch()
            adb.reload()
            kirafan.adb_mode_switch()
        elif key == 'path' and self.data['adb'][key] != value:
            self.data['adb'][key] = value
            self.__reload()
        elif key == 'serial':
            new_serial = sg.popup_get_text('new serial:', title='modify serial', default_text=self.data['adb'][key], size=30)
            if new_serial and self.data['adb'][key] != new_serial:
                self.data['adb'][key] = new_serial
                self.window[f'_adb_{key}_'].update(new_serial)
                self.__reload()

    def handle_button_event(self, key: str):
        button_event_map = {
            'Start': lambda k: self.bt_start_event(k),
            'Reset': lambda k: self.bt_reset_event(),
            'Stop once': lambda k: self.bt_stop_once_event(),
            'Visit Room': lambda k: self.bt_visit_room_event(k),
            'Cork Shop': lambda k: self.bt_cork_shop_event(k),
            'Game region': lambda k: self.bt_game_region_event(),
            'ScreenShot': lambda k: self.bt_screenshot_event(),
            'Log': lambda k: self.window['_log_area_'].update(visible=(not self.window['_log_area_'].visible)),
            'More settings': lambda k: self.window['_more_settings_area_'].update(visible=(not self.window['_more_settings_area_'].visible)),  # noqa: E501
        }
        button_str = key[len('_button_'):-1]
        button_event_map[button_str](key)

    def bt_start_event(self, key: str):
        bt_name = self.window[key].GetText()
        if bt_name == 'Start':
            self.check_configure_and_status()
            if not self.battle_job.is_alive():
                self.battle_job = Job(target=battle, args=(self.window,))
                self.battle_job.start()
            elif self.battle_job.is_pausing():
                self.battle_job.resume()
            self.update_button_status(key, 'Stop')
        elif bt_name == 'Stop':
            if self.battle_job.is_alive():
                self.update_button_status(key, bt_name)
                self.stop_safe(self.battle_job)
            self.update_button_status(key, 'Start')

    def bt_reset_event(self):
        self.__save()
        if self.data['questList']['quest_selector'] == self.tabs[self.window['_tab_group_'].get()].name:
            self.__reload()
        self.update_stop_once_status()
        self.tabs[self.window['_tab_group_'].get()].reset(self.window)
        logger.info(f'kirafan-bot: reset {self.tabs[self.window["_tab_group_"].get()].name} quest finish')

    def bt_stop_once_event(self):
        kirafan.stop_once = not kirafan.stop_once
        self.update_stop_once_status()
        logger.info(f'({str(kirafan.stop_once):>5}) kirafan-bot stop after current battle is completed')

    def bt_screenshot_event(self):
        kirafan.screenshot()

    def bt_game_region_event(self):
        new = list(game_region())
        if self.data['location'] != new:
            self.data['location'] = new
            self.__reload()

    def bt_visit_room_event(self, key: str):
        bt_name = self.window[key].GetText()
        if bt_name == 'Visit Room':
            if not self.visit_room_job.is_alive():
                self.visit_room_job = Job(target=visit_friend_room, args=(self.window,))
                self.visit_room_job.start()
            elif self.visit_room_job.is_pausing():
                self.visit_room_job.resume()
            self.update_button_status(key, 'Stop Visit')
        elif bt_name == 'Stop Visit':
            if self.visit_room_job.is_alive():
                self.update_button_status(key, bt_name)
                self.stop_safe(self.visit_room_job)
            self.update_button_status(key, 'Visit Room')

    def bt_cork_shop_event(self, key: str):
        bt_name = self.window[key].GetText()
        if bt_name == 'Cork Shop':
            if not self.cork_shop_job.is_alive():
                self.cork_shop_job = Job(target=cork_shop_exchange, args=(self.window,))
                self.cork_shop_job.start()
            elif self.cork_shop_job.is_pausing():
                self.cork_shop_job.resume()
            self.update_button_status(key, 'Stop Exchange')
        elif bt_name == 'Stop Exchange':
            if self.cork_shop_job.is_alive():
                self.update_button_status(key, bt_name)
                self.stop_safe(self.cork_shop_job)
            self.update_button_status(key, 'Cork Shop')

    def handle_update_event(self, key: str, value):
        update_event_map = {
            '_update_wave_id_': lambda v: self.find_tab_by_name(self.data['questList']['quest_selector']).update_wave_id_status(self.window, v),  # noqa: E501
            '_update_loop_count_': lambda v: self.find_tab_by_name(self.data['questList']['quest_selector']).update_loop_count_status(self.window, v),  # noqa: E501
            '_update_stop_once_': lambda v: self.update_stop_once_status(),
            '_update_button_start_': lambda v: self.update_button_status('_button_Start_', v),
            '_update_button_visit_room_': lambda v: self.update_button_status('_button_Visit Room_', v),
            '_update_button_cork_shop_': lambda v: self.update_button_status('_button_Cork Shop_', v)
        }
        update_event_map[key](value)

    def update_stop_once_status(self):
        self.window['_button_Stop once_'].Update('Cancel' if kirafan.stop_once else 'Stop once')

    def update_button_status(self, key: str, new_button: str):
        old_button = self.window[key].GetText()
        if old_button == new_button:
            self.window[key].Update(disabled=True)
            self.window['_tips_'].Update('Tips: Please wait for a while')
            self.window.Refresh()
            return

        self.window[key].Update(new_button, disabled=False)
        self.toggle_other_buttons(old_button)
        if key == '_button_Start_':
            self.window['_running_status_'].Update('' if new_button == 'Start' else self.tabs[self.window['_tab_group_'].get()].name)  # noqa: E501
        self.update_tips_information()

    def update_tips_information(self):
        if self.blocked():
            return
        if not self.data['adb']['use'] and (self.battle_job.is_alive() or self.visit_room_job.is_alive() or
                                            self.cork_shop_job.is_alive()):
            self.window['_tips_'].update('Tips: press hotkey(z+s) to stop bot')
        else:
            self.window['_tips_'].update('')
            self.window['_timer_countdown_'].update('')

    def toggle_other_buttons(self, current_button: str):
        toggle_other_buttons_map = {
            'Start': lambda: (self.window['_button_Visit Room_'].Update(disabled=True),
                              self.window['_button_Cork Shop_'].Update(disabled=True),
                              self.window['_button_Game region_'].Update(disabled=True)),
            'Visit Room': lambda: (self.window['_button_Start_'].Update(disabled=True),
                                   self.window['_button_Stop once_'].Update(disabled=True),
                                   self.window['_button_Cork Shop_'].Update(disabled=True),
                                   self.window['_button_Game region_'].Update(disabled=True)),
            'Cork Shop': lambda: (self.window['_button_Start_'].Update(disabled=True),
                                  self.window['_button_Stop once_'].Update(disabled=True),
                                  self.window['_button_Visit Room_'].Update(disabled=True),
                                  self.window['_button_Game region_'].Update(disabled=True)),
            'Stop': lambda: (self.window['_button_Visit Room_'].Update(disabled=False),
                             self.window['_button_Cork Shop_'].Update(disabled=False),
                             self.data['adb']['use'] or self.window['_button_Game region_'].Update(disabled=False)),
            'Stop Visit': lambda: (self.window['_button_Start_'].Update(disabled=False),
                                   self.window['_button_Stop once_'].Update(disabled=False),
                                   self.window['_button_Cork Shop_'].Update(disabled=False),
                                   self.data['adb']['use'] or self.window['_button_Game region_'].Update(disabled=False)),
            'Stop Exchange': lambda: (self.window['_button_Start_'].Update(disabled=False),
                                      self.window['_button_Stop once_'].Update(disabled=False),
                                      self.window['_button_Visit Room_'].Update(disabled=False),
                                      self.data['adb']['use'] or self.window['_button_Game region_'].Update(disabled=False)),
        }
        toggle_other_buttons_map[current_button]()

    def create_layout(self) -> List:
        return [
            self.__tab_group_area(),
            self.__more_settings_area(),
            self.__information_area(),
            self.__button_area(),
            self.__log_level_area()
        ]

    def __tab_group_area(self) -> List:
        self.tabs = {str(i): Tab_Frame(str(i), name, self.data['questList'][name])
                     for i, name in enumerate(filter(lambda x: x != 'quest_selector', self.data['questList'].keys()))}
        self.next_id = str(len(self.tabs))
        return [
            [sg.TabGroup([
                [sg.Tab(tab.name, tab.create_layout(), key=id) for id, tab in self.tabs.items()],
                self.__tab_plus()
            ], key='_tab_group_', selected_title_color='red2', focus_color='Any', enable_events=True)]
        ]

    def __tab_plus(self) -> List:
        tab = self.tabs[self.next_id] = Tab_Frame(self.next_id, '+', uData.create_default_quest())
        self.next_id = str(int(self.next_id) + 1)
        return [sg.Tab(tab.name, tab.create_layout(), key=tab.id)]

    def __more_settings_area(self) -> List:
        return [sg.pin(
            sg.Column([
                self.__sleep_area() + self.__set_timer_area(),
                self.__adb_area()
            ], k='_more_settings_area_', visible=False)
        )]

    def __sleep_area(self) -> List:
        self.__original_sleep = deepcopy(self.data['sleep'])
        delay = self.data['sleep']
        k = ['_sleep_click_', '_sleep_sp_', '_sleep_loadding_', '_sleep_wave_transitions_']
        layout = [[
            sg.Text('click:', pad=((5, 0), 5)),
            sg.Input(delay['click'], size=3, pad=((0, 10), 5), key=k[0], enable_events=True),
            sg.Text('sp:', pad=((5, 0), 5)),
            sg.Input(delay['sp'], size=4, pad=((0, 10), 5), key=k[1], enable_events=True),
            sg.Text('loading:', pad=((5, 0), 5)),
            sg.Input(delay['loading'], size=4, pad=((0, 10), 5), key=k[2], enable_events=True),
            sg.Text('wave transition:', pad=((5, 0), 5)),
            sg.Input(delay['wave_transitions'], size=3, pad=((0, 10), 5), key=k[3], enable_events=True)
        ]]
        return [sg.Frame('delay (s)', layout, pad=((0, 5), 5))]

    def __set_timer_area(self) -> List:
        self.__original_timer = deepcopy(self.data['set_timer'])
        timer = self.data['set_timer']
        k = ['_timer_use_', '_timer_hour_start_', '_timer_min_start_', '_timer_sec_start_',
             '_timer_hour_end_', '_timer_min_end_', '_timer_sec_end_']
        frame_layout = [[
            sg.Checkbox('use', default=timer['use'], key=k[0], enable_events=True),
            sg.Text('pause range(h:m:s):', pad=((5, 0), 5)),
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 24)], size=2, key=k[1], readonly=True, initial_value=timer['pause_range'][:2], disabled=(not timer['use']), enable_events=True), sg.Text(':', pad=(0, 0)),  # noqa: E501
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 60)], size=2, key=k[2], readonly=True, initial_value=timer['pause_range'][3:5], disabled=(not timer['use']), enable_events=True), sg.Text(':', pad=(0, 0)),  # noqa: E501
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 60)], size=2, key=k[3], readonly=True, initial_value=timer['pause_range'][6:8], disabled=(not timer['use']), enable_events=True), sg.Text('  -  ', pad=(0, 0)),  # noqa: E501
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 24)], size=2, key=k[4], readonly=True, initial_value=timer['pause_range'][9:11], disabled=(not timer['use']), enable_events=True), sg.Text(':', pad=(0, 0)),  # noqa: E501
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 60)], size=2, key=k[5], readonly=True, initial_value=timer['pause_range'][12:14], disabled=(not timer['use']), enable_events=True), sg.Text(':', pad=(0, 0)),  # noqa: E501
            sg.Spin([f'{("0" + str(i))[-2:]}' for i in range(0, 60)], size=2, key=k[6], readonly=True, initial_value=timer['pause_range'][15:17], disabled=(not timer['use']), enable_events=True)  # noqa: E501
        ]]
        return [sg.Frame('timer', frame_layout)]

    def __adb_area(self) -> List:
        adb = self.data['adb']
        frame_layout = [[
            sg.Checkbox('use', default=adb['use'], key='_adb_use_', enable_events=True),
            sg.Text('serial:', pad=((5, 0), 5)),
            sg.Input(adb['serial'], key='_adb_serial_', size=13, disabled_readonly_background_color=('white' if adb['use'] else 'gray'), disabled=True),  # noqa: E501
            sg.Text('path:', pad=((5, 0), 5)),
            sg.Input(adb['path'], key='_adb_path_', size=63, enable_events=True, disabled_readonly_background_color=('white' if adb['use'] else 'gray'), disabled=True),  # noqa: E501
            sg.FileBrowse(key='_adb_browse_', size=7, disabled=not adb['use'])
        ]]
        return [sg.Frame('adb.exe', frame_layout, pad=((0, 5), 5))]

    def __information_area(self) -> List:
        return [
            sg.Text('Running:', pad=((5, 0), 5)), sg.Text('', font=('Any', 11, 'bold'), size=40, key='_running_status_'),
            sg.Column([[sg.Text('', size=30, key='_timer_countdown_')]], expand_x=True, element_justification='center'),
            sg.Column([[sg.Text('', size=30, text_color='red2', justification='right', font=('Any', 11, 'bold'), key='_tips_')
                        ]], expand_x=True, element_justification='right')
        ]

    def __button_area(self) -> List:
        button_list = ['Start', 'Reset', 'Stop once', 'Visit Room', 'Cork Shop',
                       'Game region', 'ScreenShot', 'Log', 'More settings', 'Exit']
        return [
            sg.Column([[
                sg.Button(bt, key=f'_button_{bt}_', mouseover_colors=None, size=12, focus=True if bt == 'Reset' else None,
                          disabled=True if bt == 'Game region' and self.data['adb']['use'] else False) for bt in button_list
            ]], element_justification='right', expand_x=True)
        ]

    def __log_level_area(self) -> List:
        level = [
            sg.Column([[
                sg.Text('Log level:', pad=(0, 0)),
                sg.InputCombo([e.name for e in loglevel], default_value=self.data['loglevel'].upper(),
                              key='_log_level_', enable_events=True)
            ]], vertical_alignment='top')
        ]
        box = [sg.Multiline(size=(None, 10), key='_log_box_', pad=(0, 0), expand_x=True)]
        return [sg.pin(sg.Column([box + level], expand_x=True, k='_log_area_', visible=False), expand_x=True)]

    def stop_all_safe(self):
        self.stop_safe(self.battle_job)
        self.stop_safe(self.visit_room_job)

    def stop_safe(self, job: Job):
        if job.is_alive():
            job.gui_button_stop()
            job.join()
            job.gui_button_stop_finish()

    def blocked(self):
        if not (self.battle_job.is_not_gui_button_stop() and self.visit_room_job.is_not_gui_button_stop() and
                self.cork_shop_job.is_not_gui_button_stop()):
            return True
        return False

    def __save(self):
        uData.save_gui_setting()
        self.__original_sleep = deepcopy(self.data['sleep'])
        self.__original_timer = deepcopy(self.data['set_timer'])
        if self.window['_tab_group_'].get():  # is None if event == sg.exit
            self.tabs[self.window['_tab_group_'].get()].update_original_quest()

    def __reload(self):
        uData.reload()
        adb.reload()
        kirafan.reload()
        logger.info('kirafan-bot: reload configuration finish')
Esempio n. 9
0
def test_hotkey():
    hotkey = Hotkey('rslmptcoxkvei')
    assert hotkey.safe_exit() is None
    assert hotkey._Hotkey__user_command('1') is None
    assert hotkey._Hotkey__user_command('r') is None
    assert hotkey._Hotkey__user_command('s') is None
    assert hotkey._Hotkey__user_command('l') is None
    # assert hotkey._Hotkey__user_command('m') is None
    assert hotkey._Hotkey__user_command('t') is None
    assert hotkey._Hotkey__user_command('c') is None
    assert hotkey._Hotkey__user_command('o') is None
    # assert hotkey._Hotkey__user_command('x') is None
    assert hotkey._Hotkey__user_command('k') is None
    assert hotkey._Hotkey__user_command('k') is None  # switch back
    assert hotkey._Hotkey__user_command('v') is None
    assert hotkey._Hotkey__user_command('s') is None
    assert hotkey._Hotkey__user_command('e') is None
    assert hotkey._Hotkey__user_command('s') is None
    assert hotkey._Hotkey__user_command('i') is None
    assert os.path.exists('screenshot0.png') is True
    assert hotkey.remove_all_hotkey() is None
Esempio n. 10
0
def player(rec_dir, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir,
           app_version, debug):
    # general imports
    from time import sleep
    import logging
    from glob import glob
    from time import time, strftime, localtime

    # networking
    import zmq
    import zmq_tools

    import numpy as np

    # zmq ipc setup
    zmq_ctx = zmq.Context()
    ipc_pub = zmq_tools.Msg_Dispatcher(zmq_ctx, ipc_push_url)
    notify_sub = zmq_tools.Msg_Receiver(zmq_ctx,
                                        ipc_sub_url,
                                        topics=("notify", ))

    # log setup
    logging.getLogger("OpenGL").setLevel(logging.ERROR)
    logger = logging.getLogger()
    logger.handlers = []
    logger.setLevel(logging.NOTSET)
    logger.addHandler(zmq_tools.ZMQ_handler(zmq_ctx, ipc_push_url))
    # create logger for the context of this function
    logger = logging.getLogger(__name__)

    try:
        from background_helper import IPC_Logging_Task_Proxy

        IPC_Logging_Task_Proxy.push_url = ipc_push_url

        from tasklib.background.patches import IPCLoggingPatch

        IPCLoggingPatch.ipc_push_url = ipc_push_url

        # imports
        from file_methods import Persistent_Dict, next_export_sub_dir

        from OpenGL.GL import GL_COLOR_BUFFER_BIT

        # display
        import glfw
        from gl_utils import GLFWErrorReporting

        GLFWErrorReporting.set_default()

        # check versions for our own depedencies as they are fast-changing
        from pyglui import __version__ as pyglui_version

        from pyglui import ui, cygl
        from pyglui.cygl.utils import Named_Texture, RGBA
        import gl_utils

        # capture
        from video_capture import File_Source

        # helpers/utils
        from version_utils import parse_version
        from methods import normalize, denormalize, delta_t, get_system_info
        import player_methods as pm
        from pupil_recording import PupilRecording
        from csv_utils import write_key_value_file
        from hotkey import Hotkey

        # Plug-ins
        from plugin import Plugin, Plugin_List, import_runtime_plugins
        from plugin_manager import Plugin_Manager
        from vis_circle import Vis_Circle
        from vis_cross import Vis_Cross
        from vis_polyline import Vis_Polyline
        from vis_light_points import Vis_Light_Points
        from vis_watermark import Vis_Watermark
        from vis_fixation import Vis_Fixation

        from seek_control import Seek_Control
        from surface_tracker import Surface_Tracker_Offline

        # from marker_auto_trim_marks import Marker_Auto_Trim_Marks
        from fixation_detector import Offline_Fixation_Detector
        from log_display import Log_Display
        from annotations import Annotation_Player
        from raw_data_exporter import Raw_Data_Exporter
        from log_history import Log_History
        from pupil_producers import (
            DisabledPupilProducer,
            Pupil_From_Recording,
            Offline_Pupil_Detection,
        )
        from gaze_producer.gaze_from_recording import GazeFromRecording
        from gaze_producer.gaze_from_offline_calibration import (
            GazeFromOfflineCalibration, )
        from pupil_detector_plugins.detector_base_plugin import PupilDetectorPlugin
        from system_graphs import System_Graphs
        from system_timelines import System_Timelines
        from blink_detection import Offline_Blink_Detection
        from audio_playback import Audio_Playback
        from video_export.plugins.imotions_exporter import iMotions_Exporter
        from video_export.plugins.eye_video_exporter import Eye_Video_Exporter
        from video_export.plugins.world_video_exporter import World_Video_Exporter
        from head_pose_tracker.offline_head_pose_tracker import (
            Offline_Head_Pose_Tracker, )
        from video_capture import File_Source
        from video_overlay.plugins import Video_Overlay, Eye_Overlay

        from pupil_recording import (
            assert_valid_recording_type,
            InvalidRecordingException,
        )

        assert parse_version(pyglui_version) >= parse_version(
            "1.29"), "pyglui out of date, please upgrade to newest version"

        process_was_interrupted = False

        def interrupt_handler(sig, frame):
            import traceback

            trace = traceback.format_stack(f=frame)
            logger.debug(f"Caught signal {sig} in:\n" + "".join(trace))
            nonlocal process_was_interrupted
            process_was_interrupted = True

        signal.signal(signal.SIGINT, interrupt_handler)

        runtime_plugins = import_runtime_plugins(
            os.path.join(user_dir, "plugins"))
        runtime_plugins = [
            p for p in runtime_plugins
            if not issubclass(p, PupilDetectorPlugin)
        ]
        system_plugins = [
            Log_Display,
            Seek_Control,
            Plugin_Manager,
            System_Graphs,
            System_Timelines,
            Audio_Playback,
        ]
        user_plugins = [
            Vis_Circle,
            Vis_Fixation,
            Vis_Polyline,
            Vis_Light_Points,
            Vis_Cross,
            Vis_Watermark,
            Eye_Overlay,
            Video_Overlay,
            Offline_Fixation_Detector,
            Offline_Blink_Detection,
            Surface_Tracker_Offline,
            Raw_Data_Exporter,
            Annotation_Player,
            Log_History,
            DisabledPupilProducer,
            Pupil_From_Recording,
            Offline_Pupil_Detection,
            GazeFromRecording,
            GazeFromOfflineCalibration,
            World_Video_Exporter,
            iMotions_Exporter,
            Eye_Video_Exporter,
            Offline_Head_Pose_Tracker,
        ] + runtime_plugins

        plugins = system_plugins + user_plugins

        def consume_events_and_render_buffer():
            gl_utils.glViewport(0, 0, *g_pool.camera_render_size)
            g_pool.capture.gl_display()
            for p in g_pool.plugins:
                p.gl_display()

            gl_utils.glViewport(0, 0, *window_size)

            try:
                clipboard = glfw.get_clipboard_string(main_window).decode()
            except (AttributeError, glfw.GLFWError):
                # clipbaord is None, might happen on startup
                clipboard = ""
            g_pool.gui.update_clipboard(clipboard)
            user_input = g_pool.gui.update()
            if user_input.clipboard and user_input.clipboard != clipboard:
                # only write to clipboard if content changed
                glfw.set_clipboard_string(main_window, user_input.clipboard)

            for b in user_input.buttons:
                button, action, mods = b
                x, y = glfw.get_cursor_pos(main_window)
                pos = gl_utils.window_coordinate_to_framebuffer_coordinate(
                    main_window, x, y, cached_scale=None)
                pos = normalize(pos, g_pool.camera_render_size)
                pos = denormalize(pos, g_pool.capture.frame_size)

                for plugin in g_pool.plugins:
                    if plugin.on_click(pos, button, action):
                        break

            for key, scancode, action, mods in user_input.keys:
                for plugin in g_pool.plugins:
                    if plugin.on_key(key, scancode, action, mods):
                        break

            for char_ in user_input.chars:
                for plugin in g_pool.plugins:
                    if plugin.on_char(char_):
                        break

            glfw.swap_buffers(main_window)

        # Callback functions
        def on_resize(window, w, h):
            nonlocal window_size
            nonlocal content_scale
            if w == 0 or h == 0:
                return

            # Always clear buffers on resize to make sure that there are no overlapping
            # artifacts from previous frames.
            gl_utils.glClear(GL_COLOR_BUFFER_BIT)
            gl_utils.glClearColor(0, 0, 0, 1)

            content_scale = gl_utils.get_content_scale(window)
            framebuffer_scale = gl_utils.get_framebuffer_scale(window)
            g_pool.gui.scale = content_scale
            window_size = w, h
            g_pool.camera_render_size = w - int(
                icon_bar_width * g_pool.gui.scale), h
            g_pool.gui.update_window(*window_size)
            g_pool.gui.collect_menus()
            for p in g_pool.plugins:
                p.on_window_resize(window, *g_pool.camera_render_size)

            # Minimum window size required, otherwise parts of the UI can cause openGL
            # issues with permanent effects. Depends on the content scale, which can
            # potentially be dynamically modified, so we re-adjust the size limits every
            # time here.
            min_size = int(2 * icon_bar_width * g_pool.gui.scale /
                           framebuffer_scale)
            glfw.set_window_size_limits(
                window,
                min_size,
                min_size,
                glfw.DONT_CARE,
                glfw.DONT_CARE,
            )

            # Needed, to update the window buffer while resizing
            consume_events_and_render_buffer()

        def on_window_key(window, key, scancode, action, mods):
            g_pool.gui.update_key(key, scancode, action, mods)

        def on_window_char(window, char):
            g_pool.gui.update_char(char)

        def on_window_mouse_button(window, button, action, mods):
            g_pool.gui.update_button(button, action, mods)

        def on_pos(window, x, y):
            x, y = gl_utils.window_coordinate_to_framebuffer_coordinate(
                window, x, y, cached_scale=None)
            g_pool.gui.update_mouse(x, y)
            pos = x, y
            pos = normalize(pos, g_pool.camera_render_size)
            # Position in img pixels
            pos = denormalize(pos, g_pool.capture.frame_size)
            for p in g_pool.plugins:
                p.on_pos(pos)

        def on_scroll(window, x, y):
            g_pool.gui.update_scroll(x, y * scroll_factor)

        def on_drop(window, paths):
            for path in paths:
                try:
                    assert_valid_recording_type(path)
                    _restart_with_recording(path)
                    return
                except InvalidRecordingException as err:
                    logger.debug(str(err))

            for plugin in g_pool.plugins:
                if plugin.on_drop(paths):
                    break

        def _restart_with_recording(rec_dir):
            logger.debug("Starting new session with '{}'".format(rec_dir))
            ipc_pub.notify({
                "subject": "player_drop_process.should_start",
                "rec_dir": rec_dir
            })
            glfw.set_window_should_close(g_pool.main_window, True)

        tick = delta_t()

        def get_dt():
            return next(tick)

        recording = PupilRecording(rec_dir)
        meta_info = recording.meta_info

        # log info about Pupil Platform and Platform in player.log
        logger.info("Application Version: {}".format(app_version))
        logger.info("System Info: {}".format(get_system_info()))
        logger.debug(f"Debug flag: {debug}")

        icon_bar_width = 50
        window_size = None
        content_scale = 1.0

        # create container for globally scoped vars
        g_pool = SimpleNamespace()
        g_pool.app = "player"
        g_pool.process = "player"
        g_pool.zmq_ctx = zmq_ctx
        g_pool.ipc_pub = ipc_pub
        g_pool.ipc_pub_url = ipc_pub_url
        g_pool.ipc_sub_url = ipc_sub_url
        g_pool.ipc_push_url = ipc_push_url
        g_pool.plugin_by_name = {p.__name__: p for p in plugins}
        g_pool.camera_render_size = None

        video_path = recording.files().core().world().videos()[0].resolve()
        File_Source(
            g_pool,
            timing="external",
            source_path=video_path,
            buffered_decoding=True,
            fill_gaps=True,
        )

        # load session persistent settings
        session_settings = Persistent_Dict(
            os.path.join(user_dir, "user_settings_player"))
        if parse_version(session_settings.get("version",
                                              "0.0")) != app_version:
            logger.info(
                "Session setting are a different version of this app. I will not use those."
            )
            session_settings.clear()

        width, height = g_pool.capture.frame_size
        width += icon_bar_width
        width, height = session_settings.get("window_size", (width, height))

        window_name = f"Pupil Player: {meta_info.recording_name} - {rec_dir}"

        glfw.init()
        glfw.window_hint(glfw.SCALE_TO_MONITOR, glfw.TRUE)
        main_window = glfw.create_window(width, height, window_name, None,
                                         None)
        window_position_manager = gl_utils.WindowPositionManager()
        window_pos = window_position_manager.new_window_position(
            window=main_window,
            default_position=window_position_default,
            previous_position=session_settings.get("window_position", None),
        )
        glfw.set_window_pos(main_window, window_pos[0], window_pos[1])

        glfw.make_context_current(main_window)
        cygl.utils.init()
        g_pool.main_window = main_window

        g_pool.version = app_version
        g_pool.timestamps = g_pool.capture.timestamps
        g_pool.get_timestamp = lambda: 0.0
        g_pool.user_dir = user_dir
        g_pool.rec_dir = rec_dir
        g_pool.meta_info = meta_info
        g_pool.min_data_confidence = session_settings.get(
            "min_data_confidence", MIN_DATA_CONFIDENCE_DEFAULT)
        g_pool.min_calibration_confidence = session_settings.get(
            "min_calibration_confidence", MIN_CALIBRATION_CONFIDENCE_DEFAULT)

        # populated by producers
        g_pool.pupil_positions = pm.PupilDataBisector()
        g_pool.gaze_positions = pm.Bisector()
        g_pool.fixations = pm.Affiliator()
        g_pool.eye_movements = pm.Affiliator()

        def set_data_confidence(new_confidence):
            g_pool.min_data_confidence = new_confidence
            notification = {"subject": "min_data_confidence_changed"}
            notification["_notify_time_"] = time() + 0.8
            g_pool.ipc_pub.notify(notification)

        def do_export(_):
            left_idx = g_pool.seek_control.trim_left
            right_idx = g_pool.seek_control.trim_right
            export_range = left_idx, right_idx + 1  # exclusive range.stop
            export_ts_window = pm.exact_window(g_pool.timestamps,
                                               (left_idx, right_idx))

            export_dir = os.path.join(g_pool.rec_dir, "exports")
            export_dir = next_export_sub_dir(export_dir)

            os.makedirs(export_dir)
            logger.info('Created export dir at "{}"'.format(export_dir))

            export_info = {
                "Player Software Version":
                str(g_pool.version),
                "Data Format Version":
                meta_info.min_player_version,
                "Export Date":
                strftime("%d.%m.%Y", localtime()),
                "Export Time":
                strftime("%H:%M:%S", localtime()),
                "Frame Index Range:":
                g_pool.seek_control.get_frame_index_trim_range_string(),
                "Relative Time Range":
                g_pool.seek_control.get_rel_time_trim_range_string(),
                "Absolute Time Range":
                g_pool.seek_control.get_abs_time_trim_range_string(),
            }
            with open(os.path.join(export_dir, "export_info.csv"), "w") as csv:
                write_key_value_file(csv, export_info)

            notification = {
                "subject": "should_export",
                "range": export_range,
                "ts_window": export_ts_window,
                "export_dir": export_dir,
            }
            g_pool.ipc_pub.notify(notification)

        def reset_restart():
            logger.warning("Resetting all settings and restarting Player.")
            glfw.set_window_should_close(main_window, True)
            ipc_pub.notify({"subject": "clear_settings_process.should_start"})
            ipc_pub.notify({
                "subject": "player_process.should_start",
                "rec_dir": rec_dir,
                "delay": 2.0,
            })

        def toggle_general_settings(collapsed):
            # this is the menu toggle logic.
            # Only one menu can be open.
            # If no menu is open the menubar should collapse.
            g_pool.menubar.collapsed = collapsed
            for m in g_pool.menubar.elements:
                m.collapsed = True
            general_settings.collapsed = collapsed

        g_pool.gui = ui.UI()
        g_pool.menubar = ui.Scrolling_Menu("Settings",
                                           pos=(-500, 0),
                                           size=(-icon_bar_width, 0),
                                           header_pos="left")
        g_pool.iconbar = ui.Scrolling_Menu("Icons",
                                           pos=(-icon_bar_width, 0),
                                           size=(0, 0),
                                           header_pos="hidden")
        g_pool.timelines = ui.Container((0, 0), (0, 0), (0, 0))
        g_pool.timelines.horizontal_constraint = g_pool.menubar
        g_pool.user_timelines = ui.Timeline_Menu("User Timelines",
                                                 pos=(0.0, -150.0),
                                                 size=(0.0, 0.0),
                                                 header_pos="headline")
        g_pool.user_timelines.color = RGBA(a=0.0)
        g_pool.user_timelines.collapsed = True
        # add container that constaints itself to the seekbar height
        vert_constr = ui.Container((0, 0), (0, -50.0), (0, 0))
        vert_constr.append(g_pool.user_timelines)
        g_pool.timelines.append(vert_constr)

        def set_window_size():
            # Get current capture frame size
            f_width, f_height = g_pool.capture.frame_size

            # Get current display scale factor
            content_scale = gl_utils.get_content_scale(main_window)
            framebuffer_scale = gl_utils.get_framebuffer_scale(main_window)
            display_scale_factor = content_scale / framebuffer_scale

            # Scale the capture frame size by display scale factor
            f_width *= display_scale_factor
            f_height *= display_scale_factor

            # Increas the width to account for the added scaled icon bar width
            f_width += icon_bar_width * display_scale_factor

            # Set the newly calculated size (scaled capture frame size + scaled icon bar width)
            glfw.set_window_size(main_window, int(f_width), int(f_height))

        general_settings = ui.Growing_Menu("General", header_pos="headline")
        general_settings.append(ui.Button("Reset window size",
                                          set_window_size))
        general_settings.append(
            ui.Info_Text(
                f"Minimum Player Version: {meta_info.min_player_version}"))
        general_settings.append(
            ui.Info_Text(f"Player Version: {g_pool.version}"))
        general_settings.append(
            ui.Info_Text(
                f"Recording Software: {meta_info.recording_software_name}"))
        general_settings.append(
            ui.Info_Text(
                f"Recording Software Version: {meta_info.recording_software_version}"
            ))

        general_settings.append(
            ui.Info_Text(
                "High level data, e.g. fixations, or visualizations only consider gaze data that has an equal or higher confidence than the minimum data confidence."
            ))
        general_settings.append(
            ui.Slider(
                "min_data_confidence",
                g_pool,
                setter=set_data_confidence,
                step=0.05,
                min=0.0,
                max=1.0,
                label="Minimum data confidence",
            ))

        general_settings.append(
            ui.Button("Restart with default settings", reset_restart))

        g_pool.menubar.append(general_settings)
        icon = ui.Icon(
            "collapsed",
            general_settings,
            label=chr(0xE8B8),
            on_val=False,
            off_val=True,
            setter=toggle_general_settings,
            label_font="pupil_icons",
        )
        icon.tooltip = "General Settings"
        g_pool.iconbar.append(icon)

        user_plugin_separator = ui.Separator()
        user_plugin_separator.order = 0.35
        g_pool.iconbar.append(user_plugin_separator)

        g_pool.quickbar = ui.Stretching_Menu("Quick Bar", (0, 100),
                                             (100, -100))
        g_pool.export_button = ui.Thumb(
            "export",
            label=chr(0xE2C5),
            getter=lambda: False,
            setter=do_export,
            hotkey=Hotkey.EXPORT_START_PLAYER_HOTKEY(),
            label_font="pupil_icons",
        )
        g_pool.quickbar.extend([g_pool.export_button])
        g_pool.gui.append(g_pool.menubar)
        g_pool.gui.append(g_pool.timelines)
        g_pool.gui.append(g_pool.iconbar)
        g_pool.gui.append(g_pool.quickbar)

        # we always load these plugins
        _pupil_producer_plugins = [
            # In priority order (first is default)
            ("Pupil_From_Recording", {}),
            ("Offline_Pupil_Detection", {}),
            ("DisabledPupilProducer", {}),
        ]
        _pupil_producer_plugins = list(reversed(_pupil_producer_plugins))
        _gaze_producer_plugins = [
            # In priority order (first is default)
            ("GazeFromRecording", {}),
            ("GazeFromOfflineCalibration", {}),
        ]
        _gaze_producer_plugins = list(reversed(_gaze_producer_plugins))
        default_plugins = [
            ("Plugin_Manager", {}),
            ("Seek_Control", {}),
            ("Log_Display", {}),
            ("Raw_Data_Exporter", {}),
            ("Vis_Polyline", {}),
            ("Vis_Circle", {}),
            ("System_Graphs", {}),
            ("System_Timelines", {}),
            ("World_Video_Exporter", {}),
            *_pupil_producer_plugins,
            *_gaze_producer_plugins,
            ("Audio_Playback", {}),
        ]
        _plugins_to_load = session_settings.get("loaded_plugins", None)
        if _plugins_to_load is None:
            # If no plugins are available from a previous session,
            # then use the default plugin list
            _plugins_to_load = default_plugins
        else:
            # If there are plugins available from a previous session,
            # then prepend plugins that are required, but might have not been available before
            _plugins_to_load = [
                *_pupil_producer_plugins,
                *_gaze_producer_plugins,
                *_plugins_to_load,
            ]

        g_pool.plugins = Plugin_List(g_pool, _plugins_to_load)

        # Manually add g_pool.capture to the plugin list
        g_pool.plugins._plugins.append(g_pool.capture)
        g_pool.plugins._plugins.sort(key=lambda p: p.order)
        g_pool.capture.init_ui()

        general_settings.insert(
            -1,
            ui.Text_Input(
                "rel_time_trim_section",
                getter=g_pool.seek_control.get_rel_time_trim_range_string,
                setter=g_pool.seek_control.set_rel_time_trim_range_string,
                label="Relative time range to export",
            ),
        )
        general_settings.insert(
            -1,
            ui.Text_Input(
                "frame_idx_trim_section",
                getter=g_pool.seek_control.get_frame_index_trim_range_string,
                setter=g_pool.seek_control.set_frame_index_trim_range_string,
                label="Frame index range to export",
            ),
        )

        # Register callbacks main_window
        glfw.set_framebuffer_size_callback(main_window, on_resize)
        glfw.set_key_callback(main_window, on_window_key)
        glfw.set_char_callback(main_window, on_window_char)
        glfw.set_mouse_button_callback(main_window, on_window_mouse_button)
        glfw.set_cursor_pos_callback(main_window, on_pos)
        glfw.set_scroll_callback(main_window, on_scroll)
        glfw.set_drop_callback(main_window, on_drop)

        toggle_general_settings(True)

        g_pool.gui.configuration = session_settings.get("ui_config", {})
        # If previously selected plugin was not loaded this time, we will have an
        # expanded menubar without any menu selected. We need to ensure the menubar is
        # collapsed in this case.
        if all(submenu.collapsed for submenu in g_pool.menubar.elements):
            g_pool.menubar.collapsed = True

        # gl_state settings
        gl_utils.basic_gl_setup()
        g_pool.image_tex = Named_Texture()

        # trigger on_resize
        on_resize(main_window, *glfw.get_framebuffer_size(main_window))

        def handle_notifications(n):
            subject = n["subject"]
            if subject == "start_plugin":
                g_pool.plugins.add(g_pool.plugin_by_name[n["name"]],
                                   args=n.get("args", {}))
            elif subject.startswith("meta.should_doc"):
                ipc_pub.notify({
                    "subject": "meta.doc",
                    "actor": g_pool.app,
                    "doc": player.__doc__
                })
                for p in g_pool.plugins:
                    if (p.on_notify.__doc__
                            and p.__class__.on_notify != Plugin.on_notify):
                        ipc_pub.notify({
                            "subject": "meta.doc",
                            "actor": p.class_name,
                            "doc": p.on_notify.__doc__,
                        })

        while not glfw.window_should_close(
                main_window) and not process_was_interrupted:

            # fetch newest notifications
            new_notifications = []
            while notify_sub.new_data:
                t, n = notify_sub.recv()
                new_notifications.append(n)

            # notify each plugin if there are new notifications:
            for n in new_notifications:
                handle_notifications(n)
                for p in g_pool.plugins:
                    p.on_notify(n)

            events = {}
            # report time between now and the last loop interation
            events["dt"] = get_dt()

            # pupil and gaze positions are added by their respective producer plugins
            events["pupil"] = []
            events["gaze"] = []

            # allow each Plugin to do its work.
            for p in g_pool.plugins:
                p.recent_events(events)

            # check if a plugin need to be destroyed
            g_pool.plugins.clean()

            glfw.make_context_current(main_window)
            glfw.poll_events()
            # render visual feedback from loaded plugins
            if gl_utils.is_window_visible(main_window):

                gl_utils.glViewport(0, 0, *g_pool.camera_render_size)
                g_pool.capture.gl_display()
                for p in g_pool.plugins:
                    p.gl_display()

                gl_utils.glViewport(0, 0, *window_size)

                try:
                    clipboard = glfw.get_clipboard_string(main_window).decode()
                except (AttributeError, glfw.GLFWError):
                    # clipbaord is None, might happen on startup
                    clipboard = ""
                g_pool.gui.update_clipboard(clipboard)
                user_input = g_pool.gui.update()
                if user_input.clipboard and user_input.clipboard != clipboard:
                    # only write to clipboard if content changed
                    glfw.set_clipboard_string(main_window,
                                              user_input.clipboard)

                for b in user_input.buttons:
                    button, action, mods = b
                    x, y = glfw.get_cursor_pos(main_window)
                    pos = gl_utils.window_coordinate_to_framebuffer_coordinate(
                        main_window, x, y, cached_scale=None)
                    pos = normalize(pos, g_pool.camera_render_size)
                    pos = denormalize(pos, g_pool.capture.frame_size)

                    for plugin in g_pool.plugins:
                        if plugin.on_click(pos, button, action):
                            break

                for key, scancode, action, mods in user_input.keys:
                    for plugin in g_pool.plugins:
                        if plugin.on_key(key, scancode, action, mods):
                            break

                for char_ in user_input.chars:
                    for plugin in g_pool.plugins:
                        if plugin.on_char(char_):
                            break

                # present frames at appropriate speed
                g_pool.seek_control.wait(events["frame"].timestamp)
                glfw.swap_buffers(main_window)

        session_settings["loaded_plugins"] = g_pool.plugins.get_initializers()
        session_settings["min_data_confidence"] = g_pool.min_data_confidence
        session_settings[
            "min_calibration_confidence"] = g_pool.min_calibration_confidence
        session_settings["ui_config"] = g_pool.gui.configuration
        session_settings["window_position"] = glfw.get_window_pos(main_window)
        session_settings["version"] = str(g_pool.version)

        session_window_size = glfw.get_window_size(main_window)
        if 0 not in session_window_size:
            f_width, f_height = session_window_size
            if platform.system() in ("Windows", "Linux"):
                f_width, f_height = (
                    f_width / content_scale,
                    f_height / content_scale,
                )
            session_settings["window_size"] = int(f_width), int(f_height)

        session_settings.close()

        # de-init all running plugins
        for p in g_pool.plugins:
            p.alive = False
        g_pool.plugins.clean()

        g_pool.gui.terminate()
        glfw.destroy_window(main_window)

    except Exception:
        import traceback

        trace = traceback.format_exc()
        logger.error("Process Player crashed with trace:\n{}".format(trace))
    finally:
        logger.info("Process shutting down.")
        ipc_pub.notify({"subject": "player_process.stopped"})
        sleep(1.0)
Esempio n. 11
0
    def init_ui(self):
        self.add_menu()
        self.menu.label = "Recorder"
        self.menu_icon.order = 0.29

        self.menu.append(
            ui.Info_Text(
                'Pupil recordings are saved like this: "path_to_recordings/recording_session_name/nnn" where "nnn" is an increasing number to avoid overwrites. You can use "/" in your session name to create subdirectories.'
            ))
        self.menu.append(
            ui.Info_Text(
                'Recordings are saved to "~/pupil_recordings". You can change the path here but note that invalid input will be ignored.'
            ))
        self.menu.append(
            ui.Text_Input(
                "rec_root_dir",
                self,
                setter=self.set_rec_root_dir,
                label="Path to recordings",
            ))
        self.menu.append(
            ui.Text_Input(
                "session_name",
                self,
                setter=self.set_session_name,
                label="Recording session name",
            ))
        self.menu.append(
            ui.Switch(
                "show_info_menu",
                self,
                on_val=True,
                off_val=False,
                label="Request additional user info",
            ))
        self.menu.append(
            ui.Selector(
                "raw_jpeg",
                self,
                selection=[True, False],
                labels=["bigger file, less CPU", "smaller file, more CPU"],
                label="Compression",
            ))
        self.menu.append(
            ui.Info_Text(
                "Recording the raw eye video is optional. We use it for debugging."
            ))
        self.menu.append(
            ui.Switch("record_eye",
                      self,
                      on_val=True,
                      off_val=False,
                      label="Record eye"))
        self.button = ui.Thumb(
            "running",
            self,
            setter=self.toggle,
            label="R",
            hotkey=Hotkey.RECORDER_RUNNING_TOGGLE_CAPTURE_HOTKEY(),
        )
        self.button.on_color[:] = (1, 0.0, 0.0, 0.8)
        self.g_pool.quickbar.insert(2, self.button)

        self.low_disk_space_thumb = ui.Thumb("low_disk_warn",
                                             label="!",
                                             getter=lambda: True,
                                             setter=lambda x: None)
        self.low_disk_space_thumb.on_color[:] = (1, 0.0, 0.0, 0.8)
        self.low_disk_space_thumb.status_text = "Low disk space"
Esempio n. 12
0
 def __init__(self):
     QThread.__init__(self)
     from hotkey import Hotkey
     self.hotkey = Hotkey(self.handle_hotkey)
Esempio n. 13
0
    def init_ui(self):

        desc_text = ui.Info_Text(self._choreography_description_text())

        self.__ui_selector_choreography = ui.Selector(
            "selected_choreography_class",
            self,
            label="Choreography",
            selection_getter=self.__choreography_selection_getter,
        )

        self.__ui_selector_gazer = ui.Selector(
            "selected_gazer_class",
            self,
            label="Gaze Mapping",
            labels=[g.label for g in self.user_selectable_gazer_classes()],
            selection=self.user_selectable_gazer_classes(),
        )

        self.__ui_gazer_description_text = ui.Info_Text("")
        self._update_gazer_description_ui_text()

        best_practices_text = ui.Info_Text(
            "Read more about best practices at docs.pupil-labs.com"
        )

        custom_ui_elements = self._init_custom_menu_ui_elements()

        super().init_ui()
        self.add_menu()
        self.menu.label = self.label
        self.menu_icon.order = self.order
        self.menu_icon.tooltip = "Calibration"

        # Construct menu UI
        self.menu.append(self.__ui_selector_choreography)
        self.menu.append(desc_text)
        if len(custom_ui_elements) > 0:
            self.menu.append(ui.Separator())
            for ui_elem in custom_ui_elements:
                self.menu.append(ui_elem)
            self.menu.append(ui.Separator())
        else:
            self.menu.append(ui.Separator())
        self.menu.append(self.__ui_selector_gazer)
        self.menu.append(self.__ui_gazer_description_text)
        self.menu.append(best_practices_text)

        if self.shows_action_buttons:

            def calibration_setter(should_be_on):
                self.__signal_should_toggle_processing(
                    should_be_on=should_be_on, mode=ChoreographyMode.CALIBRATION
                )

            def validation_setter(should_be_on):
                self.__signal_should_toggle_processing(
                    should_be_on=should_be_on, mode=ChoreographyMode.VALIDATION
                )

            self.__ui_button_calibration = ui.Thumb(
                "is_active",
                self,
                label="C",
                hotkey=Hotkey.GAZE_CALIBRATION_CAPTURE_HOTKEY(),
                setter=calibration_setter,
                on_color=self._THUMBNAIL_COLOR_ON,
            )

            self.__ui_button_validation = ui.Thumb(
                "is_active",
                self,
                label="T",
                hotkey=Hotkey.GAZE_VALIDATION_CAPTURE_HOTKEY(),
                setter=validation_setter,
                on_color=self._THUMBNAIL_COLOR_ON,
            )

            self.__toggle_mode_button_visibility(
                is_visible=True, mode=ChoreographyMode.CALIBRATION
            )
            self.__toggle_mode_button_visibility(
                is_visible=True, mode=ChoreographyMode.VALIDATION
            )
Esempio n. 14
0
    def init_ui(self):
        self.add_menu()
        self.menu.label = "Fixation Detector"

        def set_max_dispersion(new_value):
            self.max_dispersion = new_value
            self.notify_all(
                {"subject": "fixation_detector.should_recalculate", "delay": 1.0}
            )

        def set_min_duration(new_value):
            self.min_duration = min(new_value, self.max_duration)
            self.notify_all(
                {"subject": "fixation_detector.should_recalculate", "delay": 1.0}
            )

        def set_max_duration(new_value):
            self.max_duration = max(new_value, self.min_duration)
            self.notify_all(
                {"subject": "fixation_detector.should_recalculate", "delay": 1.0}
            )

        def jump_next_fixation(_):
            cur_idx = self.last_frame_idx
            all_idc = [f["mid_frame_index"] for f in self.g_pool.fixations]
            if not all_idc:
                logger.warning("No fixations available")
                return
            # wrap-around index
            tar_fix = bisect_right(all_idc, cur_idx) % len(all_idc)
            self.notify_all(
                {
                    "subject": "seek_control.should_seek",
                    "index": int(self.g_pool.fixations[tar_fix]["mid_frame_index"]),
                }
            )

        def jump_prev_fixation(_):
            cur_idx = self.last_frame_idx
            all_idc = [f["mid_frame_index"] for f in self.g_pool.fixations]
            if not all_idc:
                logger.warning("No fixations available")
                return
            # wrap-around index
            tar_fix = (bisect_left(all_idc, cur_idx) - 1) % len(all_idc)
            self.notify_all(
                {
                    "subject": "seek_control.should_seek",
                    "index": int(self.g_pool.fixations[tar_fix]["mid_frame_index"]),
                }
            )

        for help_block in self.__doc__.split("\n\n"):
            help_str = help_block.replace("\n", " ").replace("  ", "").strip()
            self.menu.append(ui.Info_Text(help_str))
        self.menu.append(
            ui.Info_Text(
                "To start the export, wait until the detection has finished and press "
                "the export button or type 'e'."
            )
        )
        self.menu.append(
            ui.Info_Text(
                "Note: This plugin does not process fixations that have been calculated"
                " and recorded in real time."
            )
        )

        self.menu.append(
            ui.Slider(
                "max_dispersion",
                self,
                min=0.01,
                step=0.1,
                max=5.0,
                label="Maximum Dispersion [degrees]",
                setter=set_max_dispersion,
            )
        )
        self.menu.append(
            ui.Slider(
                "min_duration",
                self,
                min=10,
                step=10,
                max=4000,
                label="Minimum Duration [milliseconds]",
                setter=set_min_duration,
            )
        )
        self.menu.append(
            ui.Slider(
                "max_duration",
                self,
                min=10,
                step=10,
                max=4000,
                label="Maximum Duration [milliseconds]",
                setter=set_max_duration,
            )
        )
        self.menu.append(
            ui.Text_Input(
                "status", self, label="Detection progress:", setter=lambda x: None
            )
        )
        self.menu.append(ui.Switch("show_fixations", self, label="Show fixations"))
        self.current_fixation_details = ui.Info_Text("")
        self.menu.append(self.current_fixation_details)

        self.next_fix_button = ui.Thumb(
            "jump_next_fixation",
            setter=jump_next_fixation,
            getter=lambda: False,
            label=chr(0xE044),
            hotkey=Hotkey.FIXATION_NEXT_PLAYER_HOTKEY(),
            label_font="pupil_icons",
        )
        self.next_fix_button.status_text = "Next Fixation"
        self.g_pool.quickbar.append(self.next_fix_button)

        self.prev_fix_button = ui.Thumb(
            "jump_prev_fixation",
            setter=jump_prev_fixation,
            getter=lambda: False,
            label=chr(0xE045),
            hotkey=Hotkey.FIXATION_PREV_PLAYER_HOTKEY(),
            label_font="pupil_icons",
        )
        self.prev_fix_button.status_text = "Previous Fixation"
        self.g_pool.quickbar.append(self.prev_fix_button)