Source code for mytk.utils

import re
from threading import current_thread, main_thread


[docs] def is_main_thread() -> bool: """Check whether the current thread is the main thread.""" return current_thread() == main_thread()
[docs] def parse_geometry(geometry): """Parse a Tkinter geometry string into its size and offset components. Args: geometry (str | None): A geometry string such as "800x600", "+100+200", "800x600+100+200", or None. Returns: tuple: (size_str, offset_str) where either element may be None. Examples: "800x600+100+200" -> ("800x600", "+100+200") "800x600" -> ("800x600", None) "+100+200" -> (None, "+100+200") None -> (None, None) """ if not geometry: return None, None m = re.fullmatch(r"=?(\d+x\d+)?([+-]\d+[+-]\d+)?", geometry) if m: return m.group(1) or None, m.group(2) or None return None, None
[docs] def apply_window_position(widget, position, size_str=None): """Set only the +X+Y offset of a Tk or Toplevel window. The window size is never modified. In both cases the window is briefly withdrawn while the offset is computed and applied, so it never flashes at Tk's default (top-left) location before jumping to the requested spot. When *size_str* is provided the offset is applied immediately; otherwise the call is deferred to the first idle tick (after the event loop has laid out all content). Args: widget: A tk.Tk or tk.Toplevel instance. position (str): One of "center", "top-left", "top-right", "bottom-left", "bottom-right". size_str (str | None): The "WxH" portion of a geometry string (e.g. "800x600"). When None the size is measured from the widget after layout. Raises: ValueError: If *position* is not a recognised name. """ valid = {"center", "top-left", "top-right", "bottom-left", "bottom-right"} if position not in valid: raise ValueError(f"Unknown position '{position}'. Choose from: {sorted(valid)}") def _apply(w, h): screen_w = widget.winfo_screenwidth() screen_h = widget.winfo_screenheight() offsets = { "center": ((screen_w - w) // 2, (screen_h - h) // 2), "top-left": (0, 0), "top-right": (screen_w - w, 0), "bottom-left": (0, screen_h - h), "bottom-right": (screen_w - w, screen_h - h), } x, y = offsets[position] widget.geometry(f"+{x}+{y}") # position only — never overrides size # Hide the window while we compute and apply its position so the user never # sees it flash at Tk's default (top-left) location before it jumps to the # requested spot. We restore visibility only after the offset is applied, # and only if the caller had not already withdrawn it on purpose. originally_withdrawn = widget.state() == "withdrawn" widget.withdraw() if size_str: w, h = map(int, size_str.split("x")) widget.update_idletasks() _apply(w, h) if not originally_withdrawn: widget.deiconify() else: # Size not yet known; defer until after layout. def _deferred(): try: if not widget.winfo_exists(): return widget.update_idletasks() w = widget.winfo_width() h = widget.winfo_height() if w <= 1: # not yet rendered, fall back to requested size w = widget.winfo_reqwidth() h = widget.winfo_reqheight() _apply(w, h) if not originally_withdrawn: widget.deiconify() except Exception: return widget.nametowidget('.').after_idle(_deferred)
[docs] def package_app_script(filepath=None): """Bundle the utility module and an optional script into a single self-contained string.""" from inspect import currentframe, getframeinfo frameinfo = getframeinfo(currentframe()) script = "" with open(__file__, "r") as file: lines = file.readlines() embeddable_lines = lines[: frameinfo.lineno - 3] script += "".join(embeddable_lines) if filepath is not None: with open(filepath, "r") as file: lines = file.readlines() embeddable_lines = [ line for line in lines if "from mytk import *" not in line and "app_script" not in line ] script += "".join(embeddable_lines) try: import pyperclip pyperclip.copy(script) except ImportError: pass