def start_main() -> None: """Starts the TK and Trio loops. See https://github.com/richardsheridan/trio-guest/. """ def tk_func() -> None: """Called to execute the callback.""" queue.popleft()() def run_sync_soon_threadsafe(func: Callable[[], Any]) -> None: """Run the specified func in the next loop, from other threads.""" queue.append(func) TK_ROOT.call("after", "idle", tk_func_name) def run_sync_soon_not_threadsafe(func: Callable[[], Any]) -> None: """Run the specified func in the next loop.""" queue.append(func) # The zero here apparently avoids blocking the event loop if an endless stream of # callbacks is triggered. TK_ROOT.call("after", "idle", "after", 0, tk_func_name) queue: collections.deque[Callable[[], Any]] = collections.deque() tk_func_name = TK_ROOT.register(tk_func) LOGGER.debug('Starting Trio loop.') trio.lowlevel.start_guest_run( app_main, run_sync_soon_threadsafe=run_sync_soon_threadsafe, run_sync_soon_not_threadsafe=run_sync_soon_not_threadsafe, done_callback=done_callback, ) TK_ROOT.mainloop()
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 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.
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 test() -> None: """Test the GUI.""" from BEE2_config import GEN_OPTS from packages import find_packages, PACKAGE_SYS # Setup images to read from packages. print('Loading packages for images.') GEN_OPTS.load() find_packages(GEN_OPTS['Directories']['package']) img.load_filesystems(PACKAGE_SYS) print('Done.') left_frm = ttk.Frame(TK_ROOT) right_canv = tkinter.Canvas(TK_ROOT) left_frm.grid(row=0, column=0, sticky='NSEW', padx=8) right_canv.grid(row=0, column=1, sticky='NSEW', padx=8) TK_ROOT.rowconfigure(0, weight=1) TK_ROOT.columnconfigure(1, weight=1) slot_dest = [] slot_src = [] class TestItem: def __init__( self, name: str, pak_id: str, icon: str, group: str=None, group_icon: str=None, ) -> None: self.name = name self.dnd_icon = img.Handle.parse_uri(utils.PackagePath(pak_id, ('items/clean/{}.png'.format(icon))), 64, 64) self.dnd_group = group if group_icon: self.dnd_group_icon = img.Handle.parse_uri(utils.PackagePath(pak_id, 'items/clean/{}.png'.format(group_icon)), 64, 64) def __repr__(self) -> str: return '<Item {}>'.format(self.name) manager = Manager[TestItem](TK_ROOT, config_icon=True) def func(ev): def call(slot): print('Cback: ', ev, slot) return call for event in Event: manager.reg_callback(event, func(event)) PAK_CLEAN = 'BEE2_CLEAN_STYLE' PAK_ELEM = 'VALVE_TEST_ELEM' items = [ TestItem('Dropper', PAK_CLEAN, 'dropper'), TestItem('Entry', PAK_CLEAN, 'entry_door'), TestItem('Exit', PAK_CLEAN, 'exit_door'), TestItem('Large Obs', PAK_CLEAN, 'large_obs_room'), TestItem('Faith Plate', PAK_ELEM, 'faithplate'), TestItem('Standard Cube', PAK_ELEM, 'cube', 'ITEM_CUBE', 'cubes'), TestItem('Companion Cube', PAK_ELEM, 'companion_cube', 'ITEM_CUBE', 'cubes'), TestItem('Reflection Cube', PAK_ELEM, 'reflection_cube', 'ITEM_CUBE', 'cubes'), TestItem('Edgeless Cube', PAK_ELEM, 'edgeless_safety_cube', 'ITEM_CUBE', 'cubes'), TestItem('Franken Cube', PAK_ELEM, 'frankenturret', 'ITEM_CUBE', 'cubes'), TestItem('Repulsion Gel', PAK_ELEM, 'paintsplat_bounce', 'ITEM_PAINT_SPLAT', 'paints'), TestItem('Propulsion Gel', PAK_ELEM, 'paintsplat_speed', 'ITEM_PAINT_SPLAT', 'paints'), TestItem('Reflection Gel', PAK_ELEM, 'paintsplat_reflection', 'ITEM_PAINT_SPLAT', 'paints'), TestItem('Conversion Gel', PAK_ELEM, 'paintsplat_portal', 'ITEM_PAINT_SPLAT', 'paints'), TestItem('Cleansing Gel', PAK_ELEM, 'paintsplat_water', 'ITEM_PAINT_SPLAT', 'paints'), ] for y in range(8): for x in range(4): slot = manager.slot(left_frm, source=False, label=(format(x + 4*y, '02') if y < 3 else '')) slot.grid(column=x, row=y, padx=1, pady=1) slot_dest.append(slot) for i, item in enumerate(items): slot = manager.slot(right_canv, source=True, label=format(i+1, '02')) slot_src.append(slot) slot.contents = item def configure(e): manager.flow_slots(right_canv, slot_src) configure(None) right_canv.bind('<Configure>', configure) ttk.Button( TK_ROOT, text='Debug', command=lambda: print('Dest:', [slot.contents for slot in slot_dest]) ).grid(row=2, column=0) ttk.Button( TK_ROOT, text='Debug', command=lambda: print('Source:', [slot.contents for slot in slot_src]) ).grid(row=2, column=1) name_lbl = ttk.Label(TK_ROOT, text='') name_lbl.grid(row=3, column=0) def enter(slot): if slot.contents is not None: name_lbl['text'] = 'Name: ' + slot.contents.name def exit(slot): name_lbl['text'] = '' manager.reg_callback(Event.HOVER_ENTER, enter) manager.reg_callback(Event.HOVER_EXIT, exit) manager.reg_callback(Event.CONFIG, lambda slot: messagebox.showinfo('Hello World', str(slot.contents))) TK_ROOT.deiconify() 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.