예제 #1
0
    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)
예제 #2
0
 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)
예제 #3
0
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)))
예제 #4
0
파일: BEE2.py 프로젝트: BEEmod/BEE2.4
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()
예제 #5
0
 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
예제 #6
0
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()
예제 #7
0
파일: sound.py 프로젝트: seagemgames/BEE2.4
    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)
예제 #8
0
파일: sound.py 프로젝트: seagemgames/BEE2.4
        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
예제 #9
0
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)
예제 #10
0
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.
예제 #11
0
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)
예제 #12
0
파일: sound.py 프로젝트: seagemgames/BEE2.4
        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()
예제 #13
0
 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)
예제 #14
0
 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)
예제 #15
0
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
예제 #16
0
    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)