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 exit_handler(e): """When the user leaves, cancel the event.""" # We only want to cancel if the event hasn't expired already nonlocal event_id window.withdraw() if event_id is not None: TK_ROOT.after_cancel(event_id)
def _show(widget: tk.Misc, mouse_x, mouse_y) -> None: """Show the context window.""" # noinspection PyUnresolvedReferences, PyProtectedMember context_label['text'] = widget._bee2_tooltip_text # noinspection PyUnresolvedReferences, PyProtectedMember context_label['image'] = widget._bee2_tooltip_img window.deiconify() window.update_idletasks() window.lift() # We're going to position tooltips towards the center of the main window. # That way they don't tend to stick out, even in multi-window setups. # To decide where to put the tooltip, we first want the center of the # main window. cent_x = TK_ROOT.winfo_rootx() + TK_ROOT.winfo_width() / 2 cent_y = TK_ROOT.winfo_rooty() + TK_ROOT.winfo_height() / 2 x_centered = y_centered = True # If the widget is smaller than the context window, always center. if widget.winfo_width() > window.winfo_width(): if cent_x > mouse_x + CENT_DIST: # Left of center, so place right of the target x = widget.winfo_rootx() + widget.winfo_width() + PADDING x_centered = False elif cent_x < mouse_x - CENT_DIST: # Right of center, so place left of the target x = widget.winfo_rootx() - window.winfo_width() - PADDING x_centered = False if widget.winfo_height() > window.winfo_height(): if cent_y > mouse_y + CENT_DIST: # Above center, so place below target y = widget.winfo_rooty() + widget.winfo_height() + PADDING y_centered = False elif cent_y < mouse_y - CENT_DIST: # Below center, so place above target y = widget.winfo_rooty() - window.winfo_height() - PADDING y_centered = False if x_centered: # Center horizontally x = (widget.winfo_rootx() + (widget.winfo_width() - window.winfo_width()) // 2) if y_centered: y = (widget.winfo_rooty() + (widget.winfo_height() - window.winfo_height()) // 2) # If both X and Y are centered, the tooltip will appear on top of # the mouse and immediately hide. Offset it to fix that. if x_centered: if mouse_y < cent_y: y = widget.winfo_rooty() + widget.winfo_height() + PADDING else: y = widget.winfo_rooty() - window.winfo_height() - PADDING window.geometry('+{}+{}'.format(int(x), int(y)))
def start_main() -> None: LOGGER.debug('Starting Trio loop.') trio.lowlevel.start_guest_run( app_main, run_sync_soon_threadsafe=TK_ROOT.after_idle, done_callback=done_callback, ) TK_ROOT.mainloop()
def on_leave(slot: dragdrop.Slot[Signage]) -> None: """Reset the visible sign when left.""" nonlocal hover_toggle_id, hover_sign name_label['text'] = '' hover_sign = None if hover_toggle_id is not None: TK_ROOT.after_cancel(hover_toggle_id) hover_toggle_id = None preview_left['image'] = blank_sign preview_right['image'] = blank_sign
def init_application(): """Initialise when standalone.""" global window window = TK_ROOT window.title(_('Compiler Options - {}').format(utils.BEE_VERSION)) window.resizable(True, False) make_widgets() TK_ROOT.deiconify()
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 stop(self) -> None: """Cancel the music, if it's playing.""" if self.sample is None: return self.sample.pause() self.sample = None self._close_handles() self.stop_callback() if self.after is not None: TK_ROOT.after_cancel(self.after) self.after = None
def init_application() -> None: """Initialise the standalone application.""" from app import gameMan global window window = TK_ROOT TK_ROOT.title( gettext('BEEMOD {} - Backup / Restore Puzzles').format(utils.BEE_VERSION) ) init() UI['bar'] = bar = tk.Menu(TK_ROOT) window.option_add('*tearOff', False) if utils.MAC: # Name is used to make this the special 'BEE2' menu item file_menu = menus['file'] = tk.Menu(bar, name='apple') else: file_menu = menus['file'] = tk.Menu(bar) file_menu.add_command(label=gettext('New Backup'), command=ui_new_backup) file_menu.add_command(label=gettext('Open Backup'), command=ui_load_backup) file_menu.add_command(label=gettext('Save Backup'), command=ui_save_backup) file_menu.add_command(label=gettext('Save Backup As'), command=ui_save_backup_as) bar.add_cascade(menu=file_menu, label=gettext('File')) game_menu = menus['game'] = tk.Menu(bar) game_menu.add_command(label=gettext('Add Game'), command=gameMan.add_game) game_menu.add_command(label=gettext('Remove Game'), command=gameMan.remove_game) game_menu.add_separator() bar.add_cascade(menu=game_menu, label=gettext('Game')) gameMan.game_menu = game_menu from app import helpMenu # Add the 'Help' menu here too. helpMenu.make_help_menu(bar) window['menu'] = bar window.deiconify() window.update() gameMan.load() ui_new_backup() # UI.py isn't present, so we use this callback gameMan.setgame_callback = load_game gameMan.add_menu_opts(game_menu)
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 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 show_more_info(): url = selected_item.url if url is not None: try: webbrowser.open(url, new=OPEN_IN_TAB, autoraise=True) except webbrowser.Error: if messagebox.askyesno( icon="error", title="BEE2 - Error", message=_('Failed to open a web browser. Do you wish ' 'for the URL to be copied to the clipboard ' 'instead?'), detail='"{!s}"'.format(url), parent=prop_window): LOGGER.info("Saving {} to clipboard!", url) TK_ROOT.clipboard_clear() TK_ROOT.clipboard_append(url) # Either the webbrowser or the messagebox could cause the # properties to move behind the main window, so hide it # so it doesn't appear there. hide_context(None)
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 prompt( title: str, message: str, initialvalue: str = '', parent: tk.Misc = TK_ROOT, validator: Callable[[str], str] = _default_validator, ) -> Optional[str]: """Ask the user to enter a string.""" from loadScreen import suppress_screens from app import _main_loop_running with suppress_screens(): # If the main loop isn't running, this doesn't work correctly. # Probably also if it's not visible. So swap back to the old style. # It's also only a problem on Windows. if Query is None or (utils.WIN and (not _main_loop_running or not TK_ROOT.winfo_viewable())): query_cls = BasicQueryValidator else: query_cls = QueryValidator return query_cls(parent, title, message, initialvalue, validator).result
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)