def add_config_file_button(path: pathlib.Path) -> None: """ Add a button to *Settings/Config Files* that opens a file in Porcupine when it's clicked. """ get_menu("Settings/Config Files").add_command( label=path.name, command=(lambda: get_tab_manager().add_tab( tabs.FileTab.open_file(get_tab_manager(), path))), )
def open_files() -> None: paths = filedialog.askopenfilenames(**filedialog_kwargs) # "" or tuple for path in map(pathlib.Path, paths): try: tab = tabs.FileTab.open_file(get_tab_manager(), path) except (UnicodeError, OSError) as e: log.exception(f"opening '{path}' failed") utils.errordialog( type(e).__name__, "Opening failed!", traceback.format_exc()) continue get_tab_manager().add_tab(tab)
def save_file(save_as: bool) -> None: tab = get_tab_manager().select() assert isinstance(tab, tabs.FileTab) if save_as: tab.save_as() else: tab.save()
def set_enabled_based_on_tab( path: str, callback: Callable[[Optional[tabs.Tab]], bool]) -> Callable[..., None]: """Use this for disabling menu items depending on the currently selected tab. When the selected :class:`~porcupine.tabs.Tab` changes, ``callback`` will be called with the selected tab as an argument, or ``None`` if there are no tabs. If the callback returns ``False``, then the menu item given by *path* is disabled (so that it looks grayed out and can't be clicked). The *path* works similarly to :func:`get_menu`, except that it refers to a menu item rather than a submenu. For example, ``"Tools/Python/Black"`` means a menu item labelled *Black* in the *Tools/Python* menu. For example, this creates a menu item ``Foo/Bar`` and disables it whenever the currently selected tab is not a :class:`porcupine.tabs.FileTab`:: from porcupine import menubar, tabs def do_something(): ... def setup(): menubar.get_menu("Foo").add_command(label="Bar", command=do_something) menubar.set_enabled_based_on_tab("Foo/Bar", (lambda tab: isinstance(tab, tabs.FileTab))) Sometimes you need to update the enabled-ness of a menu item for other reasons than changing the currently selected tab. To do that, call the callback that this function returns. It's always called when the selected tab changes, but you can call it at other times too. The returned callback ignores all arguments given to it, which makes using it with ``.bind()`` easier. """ def update_enabledness(*junk: object) -> None: tab = get_tab_manager().select() menu = get_menu(path.rsplit("/", 1)[0] if "/" in path else None) index = _find_item(menu, path.split("/")[-1]) if index is None: raise LookupError(f"menu item {path!r} not found") menu.entryconfig(index, state=("normal" if callback(tab) else "disabled")) update_enabledness() get_tab_manager().bind("<<NotebookTabChanged>>", update_enabledness, add=True) return update_enabledness
def update_enabledness(*junk: object) -> None: tab = get_tab_manager().select() menu = get_menu(path.rsplit("/", 1)[0] if "/" in path else None) index = _find_item(menu, path.split("/")[-1]) if index is None: raise LookupError(f"menu item {path!r} not found") menu.entryconfig(index, state=("normal" if callback(tab) else "disabled"))
def on_tab_changed(junk: object = None) -> None: tab = get_tab_manager().select() menu = get_menu(path.rsplit('/', 1)[0] if '/' in path else None) index = _find_item(menu, path.split('/')[-1]) if index is None: raise LookupError(f"menu item {path!r} not found") menu.entryconfig(index, state=('normal' if callback(tab) else 'disabled'))
def open_files() -> None: paths: Sequence[str] = filedialog.askopenfilenames( **filedialog_kwargs) # type: ignore # tkinter returns '' if the user cancels, and i'm arfaid that python # devs might "fix" a future version to return None if not paths: return for path in map(pathlib.Path, paths): try: tab = tabs.FileTab.open_file(get_tab_manager(), path) except (UnicodeError, OSError) as e: log.exception("opening '%s' failed", path) utils.errordialog( type(e).__name__, "Opening failed!", traceback.format_exc()) continue get_tab_manager().add_tab(tab)
def set_enabled_based_on_tab( path: str, callback: Callable[[Optional[tabs.Tab]], bool]) -> None: """Use this for disabling menu items depending on the currently selected tab. When the selected :class:`~porcupine.tabs.Tab` changes, ``callback`` will be called with the selected tab as an argument, or ``None`` if there are no tabs. If the callback returns ``False``, then the menu item given by *path* is disabled (so that it looks grayed out and can't be clicked). The *path* works similarly to :func:`get_menu`, except that it refers to a menu item rather than a submenu. For example, ``"Tools/Python/Black"`` means a menu item labelled *Black* in the *Tools/Python* menu. For example, this creates a menu item ``Foo/Bar`` and disables it whenever the currently selected tab is not a :class:`porcupine.tabs.FileTab`:: from porcupine import menubar, tabs def do_something(): ... def setup(): menubar.get_menu("Foo").add_command(label="Bar", command=do_something) set_enabled_based_on_tab("Foo/Bar", (lambda tab: isinstance(tab, tabs.FileTab))) """ def on_tab_changed(junk: object = None) -> None: tab = get_tab_manager().select() menu = get_menu(path.rsplit('/', 1)[0] if '/' in path else None) index = _find_item(menu, path.split('/')[-1]) if index is None: raise LookupError(f"menu item {path!r} not found") menu.entryconfig(index, state=('normal' if callback(tab) else 'disabled')) on_tab_changed() get_tab_manager().bind('<<NotebookTabChanged>>', on_tab_changed, add=True)
def close_selected_tab() -> None: tab = get_tab_manager().select() assert tab is not None if tab.can_be_closed(): get_tab_manager().close_tab(tab)
def new_file() -> None: get_tab_manager().add_tab(tabs.FileTab(get_tab_manager()))