def load_backup(zip_file): """Load in a backup file.""" maps = [] puzzles = [ file[:-4] # Strip extension for file in zip_names(zip_file) if file.endswith('.p2c') ] # Each P2C init requires reading in the properties file, so this may take # some time. Use a loading screen. reading_loader.set_length('READ', len(puzzles)) LOGGER.info('Loading {} maps..', len(puzzles)) with reading_loader: for file in puzzles: new_map = P2C.from_file(file, zip_file) maps.append(new_map) LOGGER.debug( 'Loading {} map "{}"', 'coop' if new_map.is_coop else 'sp', new_map.title, ) reading_loader.step('READ') LOGGER.info('Done!') # It takes a while before the detail headers update positions, # so delay a refresh call. TK_ROOT.after(500, UI['game_details'].refresh) return maps
def check_queue(): """Update stages from the parent process.""" nonlocal force_ontop had_values = False while PIPE_REC.poll(): # Pop off all the values. had_values = True operation, scr_id, args = PIPE_REC.recv() if operation == 'init': # Create a new loadscreen. is_main, title, stages = args screen = (SplashScreen if is_main else LoadScreen)(scr_id, title, force_ontop, stages) SCREENS[scr_id] = screen elif operation == 'set_force_ontop': [force_ontop] = args for screen in SCREENS.values(): screen.win.attributes('-topmost', force_ontop) else: try: func = getattr(SCREENS[scr_id], 'op_' + operation) except AttributeError: raise ValueError('Bad command "{}"!'.format(operation)) try: func(*args) except Exception: raise Exception(operation) # Continually re-run this function in the TK loop. # If we didn't find anything in the pipe, wait longer. # Otherwise we hog the CPU. TK_ROOT.after(1 if had_values else 200, check_queue)
def fx_blockable(sound: str) -> None: """Play a sound effect. This waits for a certain amount of time between retriggering sounds so they don't overlap. """ global _play_repeat_sfx if play_sound and _play_repeat_sfx: fx(sound) _play_repeat_sfx = False TK_ROOT.after(75, _reset_fx_blockable)
def run_screen( pipe_send: multiprocessing.connection.Connection, pipe_rec: multiprocessing.connection.Connection, # Pass in various bits of translated text # so we don't need to do it here. translations, ): """Runs in the other process, with an end of a pipe for input.""" global PIPE_REC, PIPE_SEND PIPE_SEND = pipe_send PIPE_REC = pipe_rec TRANSLATION.update(translations) force_ontop = True def check_queue(): """Update stages from the parent process.""" nonlocal force_ontop had_values = False while PIPE_REC.poll(): # Pop off all the values. had_values = True operation, scr_id, args = PIPE_REC.recv() if operation == 'init': # Create a new loadscreen. is_main, title, stages = args screen = (SplashScreen if is_main else LoadScreen)(scr_id, title, force_ontop, stages) SCREENS[scr_id] = screen elif operation == 'set_force_ontop': [force_ontop] = args for screen in SCREENS.values(): screen.win.attributes('-topmost', force_ontop) else: try: func = getattr(SCREENS[scr_id], 'op_' + operation) except AttributeError: raise ValueError('Bad command "{}"!'.format(operation)) try: func(*args) except Exception: raise Exception(operation) # Continually re-run this function in the TK loop. # If we didn't find anything in the pipe, wait longer. # Otherwise we hog the CPU. TK_ROOT.after(1 if had_values else 200, check_queue) TK_ROOT.after(10, check_queue) TK_ROOT.mainloop() # Infinite loop, until the entire process tree quits.
def on_type(*args) -> None: """Re-search whenever text is typed.""" nonlocal refresh_tim, result text = search_var.get().casefold() words = text.split() if not words: refresh_cback(None) return found: set[tuple[str, int]] = set() *words, last = words for word in words: try: found |= word_to_ids[word] except KeyError: pass if last: try: for group in word_to_ids.itervalues(last): found |= group except KeyError: pass # The callback causes us to be deselected, so delay it until the user # stops typing. result = found if refresh_tim is not None: TK_ROOT.after_cancel(refresh_tim) refresh_tim = TK_ROOT.after(500, trigger_cback)
def flash_count(): """Flash the counter between 0 and 100 when on.""" should_cont = False for var in (count_brush, count_entity, count_overlay): if not var.should_flash: continue # Abort when it shouldn't be flashing if var.get() == 0: var.set(100) else: var.set(0) should_cont = True if should_cont: TK_ROOT.after(750, flash_count)
def play_sample(self, e: Event=None) -> None: pass """Play a sample of music. If music is being played it will be stopped instead. """ if self.cur_file is None: return if self.sample is not None: self.stop() return self._close_handles() with self.system: try: file = self.system[self.cur_file] except (KeyError, FileNotFoundError): self.stop_callback() LOGGER.error('Sound sample not found: "{}"', self.cur_file) return # Abort if music isn't found.. child_sys = self.system.get_system(file) # Special case raw filesystems - Pyglet is more efficient # if it can just open the file itself. if isinstance(child_sys, RawFileSystem): load_path = os.path.join(child_sys.path, file.path) self._cur_sys = self._handle = None LOGGER.debug('Loading music directly from {!r}', load_path) else: # Use the file objects directly. load_path = self.cur_file self._cur_sys = child_sys self._cur_sys.open_ref() self._handle = file.open_bin() LOGGER.debug('Loading music via {!r}', self._handle) try: sound = pyglet.media.load(load_path, self._handle) except (MediaDecodeException, MediaFormatException): self.stop_callback() LOGGER.exception('Sound sample not valid: "{}"', self.cur_file) return # Abort if music isn't found.. if self.start_time: try: sound.seek(self.start_time) except CannotSeekException: LOGGER.exception('Cannot seek in "{}"!', self.cur_file) self.sample = sound.play() self.after = TK_ROOT.after( int(sound.duration * 1000), self._finished, ) self.start_callback()
def check_queue(): """Update stages from the parent process.""" nonlocal force_ontop had_values = False try: while PIPE_REC.poll(): # Pop off all the values. had_values = True operation, scr_id, args = PIPE_REC.recv() if operation == 'init': # Create a new loadscreen. is_main, title, stages = args screen = (SplashScreen if is_main else LoadScreen)( scr_id, title, force_ontop, stages) SCREENS[scr_id] = screen elif operation == 'quit_daemon': # Shutdown. TK_ROOT.quit() return elif operation == 'set_force_ontop': [force_ontop] = args for screen in SCREENS.values(): screen.win.attributes('-topmost', force_ontop) else: try: func = getattr(SCREENS[scr_id], 'op_' + operation) except AttributeError: raise ValueError(f'Bad command "{operation}"!') try: func(*args) except Exception: raise Exception(operation) while log_pipe_rec.poll(): log_window.handle(log_pipe_rec.recv()) except BrokenPipeError: # A pipe failed, means the main app quit. Terminate ourselves. print('BG: Lost pipe!') TK_ROOT.quit() return # Continually re-run this function in the TK loop. # If we didn't find anything in the pipe, wait longer. # Otherwise we hog the CPU. TK_ROOT.after(1 if had_values else 200, check_queue)
def enter_handler(event): """Schedule showing the tooltip.""" nonlocal event_id # noinspection PyUnresolvedReferences, PyProtectedMember if targ_widget._bee2_tooltip_text or targ_widget._bee2_tooltip_img is not None: # We know it has this method from above! # noinspection PyUnresolvedReferences if check_disabled and not targ_widget.instate(('!disabled',)): return event_id = TK_ROOT.after( delay, after_complete, event.x_root, event.y_root, )
def play_sample(self, _: Event = None) -> None: """Play a sample of music. If music is being played it will be stopped instead. """ if self.cur_file is None: return if self.player is not None: self.stop() return try: file = self.system[self.cur_file] except (KeyError, FileNotFoundError): self.stop_callback() LOGGER.error('Sound sample not found: "{}"', self.cur_file) return # Abort if music isn't found.. child_sys = self.system.get_system(file) # Special case raw filesystems - Pyglet is more efficient # if it can just open the file itself. if isinstance(child_sys, RawFileSystem): load_path = os.path.join(child_sys.path, file.path) LOGGER.debug('Loading music directly from {!r}', load_path) else: # In a filesystem, we need to extract it. # SAMPLE_WRITE_PATH + the appropriate extension. sample_fname = SAMPLE_WRITE_PATH.with_suffix( os.path.splitext(self.cur_file)[1]) with file.open_bin() as fsrc, sample_fname.open('wb') as fdest: shutil.copyfileobj(fsrc, fdest) LOGGER.debug('Loading music {} as {}', self.cur_file, sample_fname) load_path = str(sample_fname) try: sound = decoder.decode(None, load_path) except Exception: self.stop_callback() LOGGER.exception('Sound sample not valid: "{}"', self.cur_file) return # Abort if music isn't found or can't be loaded. self.player = sound.play() self.after = TK_ROOT.after( int(sound.duration * 1000), self._finished, ) self.start_callback()
def hover_toggle() -> None: """Toggle between arrows and dual icons.""" nonlocal hover_arrow, hover_toggle_id hover_arrow = not hover_arrow if hover_sign is None: return if hover_arrow and sign_arrow: left = hover_sign.dnd_icon right = sign_arrow.dnd_icon else: try: left = Signage.by_id(hover_sign.prim_id or '').dnd_icon except KeyError: left = hover_sign.dnd_icon try: right = Signage.by_id(hover_sign.sec_id or '').dnd_icon except KeyError: right = IMG_BLANK img.apply(preview_left, left) img.apply(preview_right, right) hover_toggle_id = TK_ROOT.after(1000, hover_toggle)
def hover_toggle() -> None: """Toggle between arrows and dual icons.""" nonlocal hover_arrow, hover_toggle_id hover_arrow = not hover_arrow if hover_sign is None: return if hover_arrow and sign_arrow: preview_left['image'] = hover_sign.dnd_icon preview_right['image'] = sign_arrow.dnd_icon else: try: preview_left['image'] = Signage.by_id(hover_sign.prim_id or '').dnd_icon except KeyError: preview_left['image'] = hover_sign.dnd_icon try: preview_right['image'] = Signage.by_id(hover_sign.sec_id or '').dnd_icon except KeyError: preview_right['image'] = blank_sign hover_toggle_id = TK_ROOT.after(1000, hover_toggle)
def next_error(): # Use False since functions return None usually if next(err_iterator, False) is not False: TK_ROOT.after(1000, next_error)
if __name__ == '__main__': srctools.logger.init_logging() LOGGER = srctools.logger.get_logger('BEE2') init(True, log_level='DEBUG') # Generate a bunch of log messages to test the window. def errors(): # Use a generator to easily run these functions with a delay. yield LOGGER.info('Info Message') yield LOGGER.critical('Critical Message') yield LOGGER.warning('Warning') try: raise ValueError('An error') except ValueError: yield LOGGER.exception('Error message') yield LOGGER.warning('Post-Exception warning') yield LOGGER.info('Info') yield LOGGER.debug('Debug Message') err_iterator = errors() def next_error(): # Use False since functions return None usually if next(err_iterator, False) is not False: TK_ROOT.after(1000, next_error) TK_ROOT.after(1000, next_error) TK_ROOT.mainloop()
def run_background( pipe_send: multiprocessing.connection.Connection, pipe_rec: multiprocessing.connection.Connection, log_pipe_send: multiprocessing.connection.Connection, log_pipe_rec: multiprocessing.connection.Connection, # Pass in various bits of translated text # so we don't need to do it here. translations: dict, ): """Runs in the other process, with an end of a pipe for input.""" global PIPE_REC, PIPE_SEND PIPE_SEND = pipe_send PIPE_REC = pipe_rec TRANSLATION.update(translations) force_ontop = True log_window = LogWindow(translations, log_pipe_send) def check_queue(): """Update stages from the parent process.""" nonlocal force_ontop had_values = False try: while PIPE_REC.poll(): # Pop off all the values. had_values = True operation, scr_id, args = PIPE_REC.recv() if operation == 'init': # Create a new loadscreen. is_main, title, stages = args screen = (SplashScreen if is_main else LoadScreen)( scr_id, title, force_ontop, stages) SCREENS[scr_id] = screen elif operation == 'quit_daemon': # Shutdown. TK_ROOT.quit() return elif operation == 'set_force_ontop': [force_ontop] = args for screen in SCREENS.values(): screen.win.attributes('-topmost', force_ontop) else: try: func = getattr(SCREENS[scr_id], 'op_' + operation) except AttributeError: raise ValueError(f'Bad command "{operation}"!') try: func(*args) except Exception: raise Exception(operation) while log_pipe_rec.poll(): log_window.handle(log_pipe_rec.recv()) except BrokenPipeError: # A pipe failed, means the main app quit. Terminate ourselves. print('BG: Lost pipe!') TK_ROOT.quit() return # Continually re-run this function in the TK loop. # If we didn't find anything in the pipe, wait longer. # Otherwise we hog the CPU. TK_ROOT.after(1 if had_values else 200, check_queue) TK_ROOT.after(10, check_queue) TK_ROOT.mainloop() # Infinite loop, until the entire process tree quits.
def block_fx(): """Block fx_blockable() for a short time.""" global _play_repeat_sfx _play_repeat_sfx = False TK_ROOT.after(50, _reset_fx_blockable)