diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c718bd..0bd73b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 0.1.0 (2024-06-29) +1. add `tk.TextWindow` +2. + ### 0.0.9 (2024-06-26) 1. add `tk.TKit.ask_text` 2. focus_force for `tk.TKit` diff --git a/morebuiltins/__init__.py b/morebuiltins/__init__.py index 54b2811..4b10607 100644 --- a/morebuiltins/__init__.py +++ b/morebuiltins/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.9" +__version__ = "0.1.0" __all__ = [ "morebuiltins.utils", "morebuiltins.date", diff --git a/morebuiltins/tk.py b/morebuiltins/tk.py index 38b856e..dec6410 100644 --- a/morebuiltins/tk.py +++ b/morebuiltins/tk.py @@ -1,5 +1,8 @@ +import builtins import tkinter as tk import tkinter.messagebox +from threading import Thread +from tkinter import scrolledtext from typing import Any, Dict __all__ = ["TKit"] @@ -46,6 +49,7 @@ def resize_tk( fullscreen=False, toolwindow=False, disabled=False, + title=None, ): """Resize and Move the window to the given position, default to the center of the screen.""" screen_width = tk_obj.winfo_screenwidth() @@ -62,6 +66,8 @@ def resize_tk( tk_obj.attributes("-fullscreen", fullscreen) tk_obj.attributes("-toolwindow", toolwindow) tk_obj.attributes("-disabled", disabled) + if title is not None: + tk_obj.title(title) @classmethod def ask(cls, arg: Any, message="", title="", **kwargs): @@ -78,6 +84,18 @@ def ask(cls, arg: Any, message="", title="", **kwargs): else: raise TypeError() + @classmethod + def info(cls, message="", title="", **kwargs): + return cls.ask_msgbox(0, message, title, **kwargs) + + @classmethod + def warn(cls, message="", title="", **kwargs): + return cls.ask_msgbox(1, message, title, **kwargs) + + @classmethod + def error(cls, message="", title="", **kwargs): + return cls.ask_msgbox(2, message, title, **kwargs) + @classmethod def ask_msgbox(cls, arg: int, message="", title="", **kwargs): methods = ( @@ -229,6 +247,76 @@ def submit(event=None): root.mainloop() return text_var.get() + @classmethod + def text_context(cls, function, *args, **kwargs): + return TextWindow(function, *args, **kwargs) + + +class TextWindow: + def __init__(self, function, *args, **kwargs): + self.print = print + self.function = function + self.args = args + self.kwargs = kwargs + + self.result = None + self.error = None + self._shutdown = False + + def print_text(self, *args, end="\n", sep=" ", file=None, flush=False): + if flush: + self.text_box.delete("1.0", tk.END) + self.text_box.insert(tk.END, f"{sep.join(map(str, args))}{end}") + self.text_box.see(tk.END) + + def patch_print(self): + builtins.print = self.print_text + + def restore_print(self): + if builtins.print is not self.print: + builtins.print = self.print + + def run(self): + try: + self.result = self.function(*self.args, **self.kwargs) + except Exception as e: + self.error = e + finally: + self.shutdown() + + def __enter__(self): + try: + self.root = tk.Tk() + __resize_kwargs = self.kwargs.pop("__resize_kwargs", {}) + TKit.resize_tk(self.root, **__resize_kwargs) + __text_kwargs = self.kwargs.pop("__text_kwargs", {}) + self.text_box = scrolledtext.ScrolledText(self.root, **__text_kwargs) + self.text_box.pack(expand=True, fill="both") + self.text_box.focus_force() + self.patch_print() + t = Thread(target=self.run, daemon=True) + t.start() + self.root.mainloop() + finally: + self.shutdown() + if self.error: + raise self.error + + return self.result + + def shutdown(self): + try: + if not self._shutdown: + self._shutdown = True + self.restore_print() + self.root.quit() + except (tk.TclError, AttributeError): + pass + + def __exit__(self, *_): + self.root.withdraw() + self.shutdown() + def examples(): while True: @@ -259,6 +347,30 @@ def examples(): if result != "1\n": TKit.ask(2, "Wrong text %s" % repr(result)) continue + + def test_text(flush=False): + import time + + for i in range(50): + print(f"Test print flush={flush} -- {i}", flush=flush) + time.sleep(0.02) + return "OK" + + with TKit.text_context( + test_text, + flush=True, + __resize_kwargs={"title": "The Title", "toolwindow": True}, + __text_kwargs={"font": "_ 15"}, + ) as result: + TKit.info("result=%s" % result) + + with TKit.text_context( + test_text, + flush=False, + __resize_kwargs={"title": "The Title", "toolwindow": True}, + __text_kwargs={"font": "_ 15"}, + ) as result: + TKit.warn("result=%s" % result) break