From 3d0cd915e78cad10bb19eca355c0a30993885578 Mon Sep 17 00:00:00 2001 From: rdbende Date: Mon, 6 Nov 2023 18:08:35 +0100 Subject: [PATCH] huhh --- porcupine/__main__.py | 2 ++ porcupine/_ipc.py | 20 ++++++++--------- porcupine/_state.py | 52 ++++++++++++++++++++++++++----------------- porcupine/tabs.py | 15 ------------- 4 files changed, 43 insertions(+), 46 deletions(-) diff --git a/porcupine/__main__.py b/porcupine/__main__.py index 9ec41699d..c7c33381f 100644 --- a/porcupine/__main__.py +++ b/porcupine/__main__.py @@ -123,6 +123,7 @@ def main() -> None: ) args = parser.parse_args() + _state.init(args) # Prevent showing up a not-ready-yet root window to user @@ -139,6 +140,7 @@ def main() -> None: get_main_window().mainloop() finally: settings.save() + log.info("exiting Porcupine successfully") diff --git a/porcupine/_ipc.py b/porcupine/_ipc.py index 5a56099a4..94ffc422f 100644 --- a/porcupine/_ipc.py +++ b/porcupine/_ipc.py @@ -52,8 +52,7 @@ def _listener2queue(listener: connection.Listener, object_queue: queue.Queue[Any break -@contextlib.contextmanager -def session() -> Iterator[queue.Queue[Any]]: +def start_session() -> tuple[connection.Listener, queue.Queue[Any]]: """Context manager that listens for send(). Use this as a context manager: @@ -64,11 +63,12 @@ def session() -> Iterator[queue.Queue[Any]]: # the application """ message_queue: queue.Queue[Any] = queue.Queue() - with connection.Listener() as listener: - with _ADDRESS_FILE.open("w") as file: - print(listener.address, file=file) - thread = threading.Thread( - target=_listener2queue, args=[listener, message_queue], daemon=True - ) - thread.start() - yield message_queue + listener = connection.Listener() + + with _ADDRESS_FILE.open("w") as file: + print(listener.address, file=file) + + thread = threading.Thread(target=_listener2queue, args=[listener, message_queue], daemon=True) + thread.start() + + return listener, message_queue diff --git a/porcupine/_state.py b/porcupine/_state.py index ea87dcb0d..709d2ca7c 100644 --- a/porcupine/_state.py +++ b/porcupine/_state.py @@ -9,9 +9,13 @@ import tkinter import types from pathlib import Path -from typing import Any, Callable, Type +from typing import TYPE_CHECKING, Any, Callable, Iterable, Type +import queue + +from multiprocessing import connection from porcupine import _ipc, images, tabs, utils +from porcupine.tabs import FileTab # Windows resolution if sys.platform == "win32": @@ -34,6 +38,7 @@ class _State: tab_manager: tabs.TabManager quit_callbacks: list[Callable[[], bool]] parsed_args: Any # not None + ipc_session: connection.Listener # global state makes some things a lot easier (I'm sorry) @@ -46,10 +51,6 @@ def _log_tkinter_error( log.error("Error in tkinter callback", exc_info=(exc, val, tb)) -class Quit: - ... - - def open_files(files: Iterable[str]) -> None: tabmanager = get_tab_manager() for path_string in files: @@ -61,24 +62,27 @@ def open_files(files: Iterable[str]) -> None: # ^D # bla bla # ^D - tabmanager.file_queue.put(sys.stdin.read()) + tabmanager.add_tab(FileTab(tabmanager, content=sys.stdin.read())) else: - tabmanager.file_queue.put(Path(path_string)) + tabmanager.open_file(Path(path_string)) + + +Quit = object() - tabmanager.event_generate("<>") +def listen_for_files(message_queue: queue.Queue): + try: + message = message_queue.get_nowait() + except queue.Empty: + message = None + else: + try: + open_files([message]) + except Exception as e: + log.error(e) -def listen_for_files(): - with _ipc.session() as ipc_message_queue: - while True: - message = ipc_message_queue.get() - if message is Quit: - break - else: - try: - open_files([message]) - except Exception as e: - log.error(e) + if message is not Quit: + _get_state().root.after(500, listen_for_files, message_queue) # undocumented on purpose, don't use in plugins @@ -93,9 +97,9 @@ def init(args: Any) -> None: try: _ipc.send(args.files) except ConnectionRefusedError: - thread = threading.Thread(target=listen_for_files, daemon=True).start() + ipc_session, message_queue = _ipc.start_session() else: - log.error("another instance of Porcupine is already running, files were sent to it") + log.info("another instance of Porcupine is already running, files were sent to it") sys.exit() root = tkinter.Tk(className="Porcupine") # class name shows up in my alt+tab list @@ -126,7 +130,11 @@ def init(args: Any) -> None: tab_manager=tab_manager, quit_callbacks=[], parsed_args=args, + ipc_session=ipc_session, ) + + listen_for_files(message_queue) + log.debug("init() done") @@ -187,6 +195,8 @@ def quit() -> None: _ipc.send([Quit]) + _get_state().ipc_session.close() + for tab in get_tab_manager().tabs(): get_tab_manager().close_tab(tab) get_main_window().destroy() diff --git a/porcupine/tabs.py b/porcupine/tabs.py index 42eb34260..0ec76c3aa 100644 --- a/porcupine/tabs.py +++ b/porcupine/tabs.py @@ -9,7 +9,6 @@ import itertools import logging import os -import queue import tkinter import traceback from pathlib import Path @@ -87,11 +86,9 @@ class TabManager(ttk.Notebook): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.file_queue = queue.Queue() self.bind("<>", self._on_tab_selected, add=True) self.bind("<>", self._on_fs_changed, add=True) - self.bind("<>", self.open_from_queue, add=True) self.winfo_toplevel().bind("", self._handle_main_window_focus, add=True) # the string is call stack for adding callback @@ -162,18 +159,6 @@ def tabs(self) -> tuple[Tab, ...]: # strings instead of widget objects return tuple(self.nametowidget(tab) for tab in super().tabs()) - def open_from_queue(self, event: tkinter.Event): - while True: - try: - to_topen = self.file_queue.get(False) - except queue.Empty: - break - - if isinstance(to_topen, str): - self.add_tab(FileTab(self, content=to_topen)) - else: - self.open_file(to_topen) - def open_file(self, path: Path) -> FileTab | None: """Add a :class:`FileTab` for editing a file and select it.