update
This commit is contained in:
@@ -63,6 +63,124 @@ def add_tooltip(widget, text):
|
||||
return ToolTip(widget, text)
|
||||
|
||||
|
||||
# ─── AzA-Werkzeugfenster: Action-Buttons (lesbarer Disabled-State) ───
|
||||
#
|
||||
# Regel für AzA-Toolfenster:
|
||||
# - Buttons immer lesbar (aktiv + deaktiviert)
|
||||
# - Disabled: heller Hintergrund, dunkler Text (kein ausgewaschenes Weiss)
|
||||
# - Blau = normale Aktion, Rot = destruktiv
|
||||
# - Untere Actionbar fix; Schliessen gut sichtbar
|
||||
|
||||
TOOL_ACTION_BTN_STYLES: dict[str, dict[str, dict[str, str]]] = {
|
||||
"primary": {
|
||||
"enabled": {
|
||||
"bg": "#5B8DB3",
|
||||
"fg": "#FFFFFF",
|
||||
"activebackground": "#4A7A9C",
|
||||
"activeforeground": "#FFFFFF",
|
||||
},
|
||||
"disabled": {
|
||||
"bg": "#DDE8F2",
|
||||
"fg": "#2F5570",
|
||||
"disabledforeground": "#2F5570",
|
||||
"activebackground": "#DDE8F2",
|
||||
"activeforeground": "#2F5570",
|
||||
},
|
||||
},
|
||||
"secondary": {
|
||||
"enabled": {
|
||||
"bg": "#3A6F8F",
|
||||
"fg": "#FFFFFF",
|
||||
"activebackground": "#2F5E7A",
|
||||
"activeforeground": "#FFFFFF",
|
||||
},
|
||||
"disabled": {
|
||||
"bg": "#DDE8F2",
|
||||
"fg": "#3A5F75",
|
||||
"disabledforeground": "#3A5F75",
|
||||
"activebackground": "#DDE8F2",
|
||||
"activeforeground": "#3A5F75",
|
||||
},
|
||||
},
|
||||
"danger": {
|
||||
"enabled": {
|
||||
"bg": "#C04040",
|
||||
"fg": "#FFFFFF",
|
||||
"activebackground": "#A83838",
|
||||
"activeforeground": "#FFFFFF",
|
||||
},
|
||||
"disabled": {
|
||||
"bg": "#F0DDDD",
|
||||
"fg": "#8B3535",
|
||||
"disabledforeground": "#8B3535",
|
||||
"activebackground": "#F0DDDD",
|
||||
"activeforeground": "#8B3535",
|
||||
},
|
||||
},
|
||||
"close": {
|
||||
"enabled": {
|
||||
"bg": "#5B8DB3",
|
||||
"fg": "#FFFFFF",
|
||||
"activebackground": "#4A7A9C",
|
||||
"activeforeground": "#FFFFFF",
|
||||
},
|
||||
"disabled": {
|
||||
"bg": "#DDE8F2",
|
||||
"fg": "#2F5570",
|
||||
"disabledforeground": "#2F5570",
|
||||
"activebackground": "#DDE8F2",
|
||||
"activeforeground": "#2F5570",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def create_tool_action_button(
|
||||
parent,
|
||||
text: str,
|
||||
command,
|
||||
*,
|
||||
kind: str = "primary",
|
||||
font=None,
|
||||
padx: int = 10,
|
||||
pady: int = 5,
|
||||
width=None,
|
||||
) -> tk.Button:
|
||||
"""Action-Button für AzA-Werkzeugfenster mit lesbarem Disabled-State."""
|
||||
styles = TOOL_ACTION_BTN_STYLES.get(kind, TOOL_ACTION_BTN_STYLES["primary"])
|
||||
kw: dict = {
|
||||
"text": text,
|
||||
"command": command,
|
||||
"font": font or ("Segoe UI", 8, "bold"),
|
||||
"relief": "flat",
|
||||
"bd": 0,
|
||||
"padx": padx,
|
||||
"pady": pady,
|
||||
"cursor": "hand2",
|
||||
**styles["enabled"],
|
||||
}
|
||||
if width is not None:
|
||||
kw["width"] = width
|
||||
btn = tk.Button(parent, **kw)
|
||||
btn._aza_btn_kind = kind # type: ignore[attr-defined]
|
||||
return btn
|
||||
|
||||
|
||||
def set_tool_action_button_enabled(btn: tk.Button, enabled: bool) -> None:
|
||||
"""Aktiviert/deaktiviert Action-Button — Text bleibt lesbar."""
|
||||
kind = str(getattr(btn, "_aza_btn_kind", "primary") or "primary")
|
||||
styles = TOOL_ACTION_BTN_STYLES.get(kind, TOOL_ACTION_BTN_STYLES["primary"])
|
||||
style = styles["enabled"] if enabled else styles["disabled"]
|
||||
try:
|
||||
btn.config(
|
||||
state="normal" if enabled else "disabled",
|
||||
cursor="hand2" if enabled else "arrow",
|
||||
**style,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ─── Fenster-Geometrie ───
|
||||
|
||||
def center_window(window, width=None, height=None):
|
||||
@@ -81,6 +199,586 @@ def center_window(window, width=None, height=None):
|
||||
window.geometry(f"{width}x{height}+{x}+{y}")
|
||||
|
||||
|
||||
def _safe_untopmost_tool(window) -> None:
|
||||
try:
|
||||
if window.winfo_exists():
|
||||
window.attributes("-topmost", False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def clamp_window_position(
|
||||
x: int,
|
||||
y: int,
|
||||
width: int,
|
||||
height: int,
|
||||
*,
|
||||
screen_w: int | None = None,
|
||||
screen_h: int | None = None,
|
||||
margin: int = 8,
|
||||
) -> tuple[int, int]:
|
||||
"""Begrenzt Fensterposition auf sichtbaren Bildschirmbereich."""
|
||||
sw = screen_w if screen_w is not None else 1920
|
||||
sh = screen_h if screen_h is not None else 1080
|
||||
max_x = max(margin, sw - width - margin)
|
||||
max_y = max(margin, sh - height - margin)
|
||||
return max(margin, min(int(x), max_x)), max(margin, min(int(y), max_y))
|
||||
|
||||
|
||||
def get_work_area_at_point(x: int, y: int) -> tuple[int, int, int, int]:
|
||||
"""Arbeitsfläche (left, top, right, bottom) des Monitors unter dem Punkt."""
|
||||
try:
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
|
||||
user32 = ctypes.windll.user32
|
||||
MONITOR_DEFAULTTONEAREST = 2
|
||||
|
||||
class RECT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("left", wintypes.LONG),
|
||||
("top", wintypes.LONG),
|
||||
("right", wintypes.LONG),
|
||||
("bottom", wintypes.LONG),
|
||||
]
|
||||
|
||||
class MONITORINFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbSize", wintypes.DWORD),
|
||||
("rcMonitor", RECT),
|
||||
("rcWork", RECT),
|
||||
("dwFlags", wintypes.DWORD),
|
||||
]
|
||||
|
||||
class POINT(ctypes.Structure):
|
||||
_fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)]
|
||||
|
||||
pt = POINT(int(x), int(y))
|
||||
hmon = user32.MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST)
|
||||
mi = MONITORINFO()
|
||||
mi.cbSize = ctypes.sizeof(MONITORINFO)
|
||||
user32.GetMonitorInfoW(hmon, ctypes.byref(mi))
|
||||
r = mi.rcWork
|
||||
return (int(r.left), int(r.top), int(r.right), int(r.bottom))
|
||||
except Exception:
|
||||
return (0, 0, 1920, 1080)
|
||||
|
||||
|
||||
def clamp_window_position_to_work_area(
|
||||
x: int,
|
||||
y: int,
|
||||
width: int,
|
||||
height: int,
|
||||
work_area: tuple[int, int, int, int],
|
||||
*,
|
||||
margin: int = 8,
|
||||
) -> tuple[int, int]:
|
||||
"""Begrenzt Position auf Monitor-Arbeitsfläche (Taskleiste berücksichtigt)."""
|
||||
left, top, right, bottom = work_area
|
||||
work_w = max(1, int(right) - int(left))
|
||||
work_h = max(1, int(bottom) - int(top))
|
||||
rel_x = int(x) - int(left)
|
||||
rel_y = int(y) - int(top)
|
||||
rel_x, rel_y = clamp_window_position(
|
||||
rel_x, rel_y, width, height, screen_w=work_w, screen_h=work_h, margin=margin,
|
||||
)
|
||||
return int(left) + rel_x, int(top) + rel_y
|
||||
|
||||
|
||||
def parse_geometry_size(geometry: str) -> tuple[int, int]:
|
||||
"""Liest Breite/Höhe aus Tk-Geometry-String."""
|
||||
try:
|
||||
part = str(geometry or "").split("+", 1)[0]
|
||||
w_s, h_s = part.split("x", 1)
|
||||
return max(1, int(w_s)), max(1, int(h_s))
|
||||
except Exception:
|
||||
return 680, 560
|
||||
|
||||
|
||||
def _widget_anchor_offset_in_toplevel(app: tk.Misc, anchor_widget: tk.Misc) -> tuple[int, int] | None:
|
||||
"""Relativer Ankerpunkt (Mitte) eines Widgets im Hauptfenster."""
|
||||
try:
|
||||
if anchor_widget is None or not anchor_widget.winfo_exists():
|
||||
return None
|
||||
app.update_idletasks()
|
||||
anchor_widget.update_idletasks()
|
||||
ax = int(anchor_widget.winfo_rootx()) + max(1, int(anchor_widget.winfo_width())) // 2
|
||||
ay = int(anchor_widget.winfo_rooty()) + max(1, int(anchor_widget.winfo_height())) // 2
|
||||
ox = ax - int(app.winfo_rootx())
|
||||
oy = ay - int(app.winfo_rooty())
|
||||
return ox, oy
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def restore_main_window_at_cursor(
|
||||
app: tk.Misc,
|
||||
cursor_x: int,
|
||||
cursor_y: int,
|
||||
*,
|
||||
anchor_widget: tk.Misc | None = None,
|
||||
fallback_anchor: str = "top_right",
|
||||
) -> None:
|
||||
"""Hauptfenster an Cursorposition wiederherstellen (Größe beibehalten)."""
|
||||
cx, cy = int(cursor_x), int(cursor_y)
|
||||
was_zoomed = bool(getattr(app, "_mini_restore_was_zoomed", False))
|
||||
|
||||
try:
|
||||
app.deiconify()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if was_zoomed:
|
||||
try:
|
||||
work = get_work_area_at_point(cx, cy)
|
||||
left, top = int(work[0]), int(work[1])
|
||||
width, height = parse_geometry_size(
|
||||
str(getattr(app, "_mini_restore_geometry", "") or "")
|
||||
)
|
||||
app.geometry(f"{width}x{height}+{left}+{top}")
|
||||
app.update_idletasks()
|
||||
app.state("zoomed")
|
||||
except Exception:
|
||||
try:
|
||||
app.state("zoomed")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if hasattr(app, "_apply_main_topmost_state"):
|
||||
app._apply_main_topmost_state()
|
||||
app.lift()
|
||||
app.focus_force()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
geom = getattr(app, "_mini_restore_geometry", None) or ""
|
||||
width, height = parse_geometry_size(str(geom) if geom else "")
|
||||
if width <= 1 or height <= 1:
|
||||
try:
|
||||
app.update_idletasks()
|
||||
width = max(680, int(app.winfo_width() or 680))
|
||||
height = max(560, int(app.winfo_height() or 560))
|
||||
except Exception:
|
||||
width, height = 680, 560
|
||||
|
||||
offset = _widget_anchor_offset_in_toplevel(app, anchor_widget)
|
||||
if offset is None:
|
||||
btn = getattr(app, "_btn_mini_record", None)
|
||||
offset = _widget_anchor_offset_in_toplevel(app, btn)
|
||||
if offset is None:
|
||||
btn = getattr(app, "_btn_minimize", None)
|
||||
offset = _widget_anchor_offset_in_toplevel(app, btn)
|
||||
if offset is None:
|
||||
if fallback_anchor == "top_left":
|
||||
offset = (24, 24)
|
||||
else:
|
||||
offset = (max(width - 24, 24), 24)
|
||||
|
||||
win_x = cx - int(offset[0])
|
||||
win_y = cy - int(offset[1])
|
||||
work = get_work_area_at_point(cx, cy)
|
||||
win_x, win_y = clamp_window_position_to_work_area(
|
||||
win_x, win_y, width, height, work,
|
||||
)
|
||||
|
||||
try:
|
||||
app.geometry(f"{width}x{height}+{win_x}+{win_y}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
app.update_idletasks()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if hasattr(app, "_apply_main_topmost_state"):
|
||||
app._apply_main_topmost_state()
|
||||
elif hasattr(app, "lift"):
|
||||
app.lift()
|
||||
app.lift()
|
||||
app.focus_force()
|
||||
except Exception:
|
||||
try:
|
||||
app.lift()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def restore_tool_window_at_cursor(
|
||||
window: tk.Misc,
|
||||
cursor_x: int,
|
||||
cursor_y: int,
|
||||
*,
|
||||
anchor_widget: tk.Misc | None = None,
|
||||
fallback_anchor: str = "top_right",
|
||||
) -> None:
|
||||
"""Tool-Fenster (Diktat etc.) an Cursorposition wiederherstellen (Größe beibehalten)."""
|
||||
cx, cy = int(cursor_x), int(cursor_y)
|
||||
|
||||
try:
|
||||
window.deiconify()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
geom = getattr(window, "_mini_restore_geometry", None) or ""
|
||||
width, height = parse_geometry_size(str(geom) if geom else "")
|
||||
if width <= 1 or height <= 1:
|
||||
try:
|
||||
window.update_idletasks()
|
||||
width = max(int(window.winfo_width() or 420), 420)
|
||||
height = max(int(window.winfo_height() or 380), 380)
|
||||
except Exception:
|
||||
width, height = 420, 380
|
||||
|
||||
offset = _widget_anchor_offset_in_toplevel(window, anchor_widget)
|
||||
if offset is None:
|
||||
if fallback_anchor == "top_left":
|
||||
offset = (24, 24)
|
||||
else:
|
||||
offset = (max(width - 24, 24), 24)
|
||||
|
||||
win_x = cx - int(offset[0])
|
||||
win_y = cy - int(offset[1])
|
||||
work = get_work_area_at_point(cx, cy)
|
||||
win_x, win_y = clamp_window_position_to_work_area(
|
||||
win_x, win_y, width, height, work,
|
||||
)
|
||||
|
||||
try:
|
||||
window.geometry(f"{width}x{height}+{win_x}+{win_y}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
window.update_idletasks()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if bool(getattr(window, "_tool_pinned", False)):
|
||||
apply_tool_window_pin(window, True)
|
||||
window.lift()
|
||||
window.focus_force()
|
||||
except Exception:
|
||||
try:
|
||||
window.lift()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def apply_tool_window_pin(window: tk.Misc, pinned: bool) -> None:
|
||||
"""Tool-Fenster-Pin (eigener State, unabhängig vom Hauptfenster)."""
|
||||
try:
|
||||
window._tool_pinned = bool(pinned)
|
||||
window.attributes("-topmost", bool(pinned))
|
||||
if pinned:
|
||||
window.lift()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def toggle_tool_window_pin(window: tk.Misc) -> bool:
|
||||
new_val = not bool(getattr(window, "_tool_pinned", False))
|
||||
apply_tool_window_pin(window, new_val)
|
||||
return new_val
|
||||
|
||||
|
||||
def refresh_tool_pin_button(btn: tk.Misc | None, pinned: bool) -> None:
|
||||
if btn is None:
|
||||
return
|
||||
try:
|
||||
btn.configure(
|
||||
text=("📌" if pinned else "📍"),
|
||||
fg=("#FFFFFF" if pinned else "#E8F4FA"),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _restore_tool_topmost(tool_win: tk.Misc | None) -> None:
|
||||
if tool_win is None:
|
||||
return
|
||||
try:
|
||||
if not tool_win.winfo_exists():
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
if getattr(tool_win, "_tool_pinned", None) is not None:
|
||||
apply_tool_window_pin(tool_win, bool(getattr(tool_win, "_tool_pinned", False)))
|
||||
|
||||
|
||||
def add_tool_pin_button(
|
||||
header: tk.Misc,
|
||||
window: tk.Misc,
|
||||
*,
|
||||
bg: str,
|
||||
side: str = "right",
|
||||
padx: tuple[int, int] | int = (0, 4),
|
||||
) -> tk.Label:
|
||||
"""Pinnnadel für Tool-Fenster (Session-State, nicht persistent)."""
|
||||
window._tool_pinned = False
|
||||
btn = tk.Label(
|
||||
header,
|
||||
text="📍",
|
||||
font=("Segoe UI Emoji", 12),
|
||||
bg=bg,
|
||||
fg="#E8F4FA",
|
||||
cursor="hand2",
|
||||
padx=8,
|
||||
)
|
||||
btn.pack(side=side, padx=padx)
|
||||
|
||||
def _toggle(_evt=None):
|
||||
pinned = toggle_tool_window_pin(window)
|
||||
refresh_tool_pin_button(btn, pinned)
|
||||
|
||||
btn.bind("<Button-1>", _toggle)
|
||||
tip = ToolTip(btn, "Fenster immer im Vordergrund")
|
||||
|
||||
def _refresh_tip(_evt=None):
|
||||
pinned = bool(getattr(window, "_tool_pinned", False))
|
||||
tip.text = "Fixierung lösen" if pinned else "Fenster immer im Vordergrund"
|
||||
|
||||
btn.bind("<Enter>", _refresh_tip, add="+")
|
||||
window._tool_pin_btn = btn
|
||||
return btn
|
||||
|
||||
|
||||
def bring_tool_window_to_front(window, *, flash_ms: int = 300) -> None:
|
||||
"""Kurz in den Vordergrund (topmost-Flash), ohne dauerhaftes Always-on-top."""
|
||||
if getattr(window, "_main_pinned", None) is not None:
|
||||
try:
|
||||
window.lift()
|
||||
if hasattr(window, "_apply_main_topmost_state"):
|
||||
window._apply_main_topmost_state()
|
||||
else:
|
||||
window.attributes("-topmost", bool(getattr(window, "_main_pinned", False)))
|
||||
window.focus_force()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
try:
|
||||
window.lift()
|
||||
window.attributes("-topmost", True)
|
||||
if flash_ms > 0:
|
||||
window.after(flash_ms, lambda: _safe_untopmost_tool(window))
|
||||
window.focus_force()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def center_tool_window(
|
||||
window,
|
||||
width: int | None = None,
|
||||
height: int | None = None,
|
||||
*,
|
||||
parent=None,
|
||||
y_ratio: float = 0.08,
|
||||
vertical_center: bool = False,
|
||||
bring_to_front: bool = True,
|
||||
) -> None:
|
||||
"""Zentriert AzA-Werkzeugfenster horizontal; optional vertikal mittig."""
|
||||
try:
|
||||
window.update_idletasks()
|
||||
except Exception:
|
||||
pass
|
||||
if width is None or height is None:
|
||||
try:
|
||||
geom = window.geometry().split("+")[0].split("x")
|
||||
if width is None:
|
||||
width = int(geom[0])
|
||||
if height is None:
|
||||
height = int(geom[1])
|
||||
except Exception:
|
||||
width = width or 400
|
||||
height = height or 300
|
||||
if parent is not None:
|
||||
try:
|
||||
window.transient(parent)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
anchor = parent if parent is not None else window
|
||||
sw = anchor.winfo_screenwidth()
|
||||
sh = anchor.winfo_screenheight()
|
||||
sx = anchor.winfo_rootx()
|
||||
sy = anchor.winfo_rooty()
|
||||
pw = max(int(anchor.winfo_width() or 0), 1)
|
||||
ph = max(int(anchor.winfo_height() or 0), 1)
|
||||
if parent is not None and pw > 1 and ph > 1:
|
||||
x = sx + max(0, (pw - width) // 2)
|
||||
if vertical_center:
|
||||
y = sy + max(0, (ph - height) // 2)
|
||||
else:
|
||||
y = sy + max(0, int(ph * y_ratio))
|
||||
else:
|
||||
x = sx + max(0, (sw - width) // 2)
|
||||
if vertical_center:
|
||||
y = sy + max(0, (sh - height) // 2)
|
||||
else:
|
||||
y = sy + max(0, int(sh * y_ratio))
|
||||
x, y = clamp_window_position(x, y, width, height, sw, sh)
|
||||
window.geometry(f"{width}x{height}+{x}+{y}")
|
||||
except Exception:
|
||||
try:
|
||||
window.geometry(f"{width}x{height}")
|
||||
except Exception:
|
||||
pass
|
||||
if bring_to_front:
|
||||
bring_tool_window_to_front(window)
|
||||
|
||||
|
||||
# ─── Modale Dialoge / Messageboxes (immer sichtbar über gepinntem Hauptfenster) ───
|
||||
|
||||
|
||||
def _resolve_toplevel_parent(widget) -> tk.Misc | None:
|
||||
if widget is None:
|
||||
return None
|
||||
try:
|
||||
if widget.winfo_exists():
|
||||
return widget.winfo_toplevel()
|
||||
except Exception:
|
||||
pass
|
||||
return widget
|
||||
|
||||
|
||||
def _find_main_app_window(widget) -> tk.Misc | None:
|
||||
w = widget
|
||||
while w is not None:
|
||||
if getattr(w, "_main_pinned", None) is not None:
|
||||
return w
|
||||
try:
|
||||
w = w.master
|
||||
except Exception:
|
||||
break
|
||||
return None
|
||||
|
||||
|
||||
def _restore_main_topmost(main) -> None:
|
||||
if main is None:
|
||||
return
|
||||
try:
|
||||
if hasattr(main, "_apply_main_topmost_state"):
|
||||
main._apply_main_topmost_state()
|
||||
elif getattr(main, "_main_pinned", None) is not None:
|
||||
main.attributes("-topmost", bool(main._main_pinned))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def prepare_dialog_parent(parent) -> tk.Misc | None:
|
||||
"""Parent-Toplevel für Dialog sichtbar nach vorne holen."""
|
||||
parent_tl = _resolve_toplevel_parent(parent)
|
||||
if parent_tl is None:
|
||||
return None
|
||||
try:
|
||||
parent_tl.lift()
|
||||
parent_tl.focus_force()
|
||||
parent_tl.update_idletasks()
|
||||
except Exception:
|
||||
pass
|
||||
return parent_tl
|
||||
|
||||
|
||||
def make_modal_topmost(dialog, parent=None, *, grab: bool = True) -> None:
|
||||
"""Modaler Toplevel-Dialog: transient, grab, lift, topmost (über Pin-Hauptfenster)."""
|
||||
parent_tl = prepare_dialog_parent(parent)
|
||||
if parent_tl is not None:
|
||||
try:
|
||||
dialog.transient(parent_tl)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
dialog.update_idletasks()
|
||||
dialog.attributes("-topmost", True)
|
||||
dialog.lift()
|
||||
dialog.focus_force()
|
||||
if grab:
|
||||
dialog.grab_set()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def release_modal_dialog(dialog, parent=None) -> None:
|
||||
"""Grab/Topmost lösen; Hauptfenster-Pin-State wiederherstellen."""
|
||||
try:
|
||||
if dialog.winfo_exists():
|
||||
dialog.grab_release()
|
||||
dialog.attributes("-topmost", False)
|
||||
except Exception:
|
||||
pass
|
||||
main = _find_main_app_window(_resolve_toplevel_parent(parent or dialog))
|
||||
_restore_main_topmost(main)
|
||||
|
||||
|
||||
def _run_with_visible_parent(parent, func):
|
||||
"""Messagebox/simpledialog: Parent kurz topmost, danach Pin wiederherstellen."""
|
||||
parent_tl = prepare_dialog_parent(parent)
|
||||
main = _find_main_app_window(parent_tl) if parent_tl else None
|
||||
if parent_tl is not None:
|
||||
try:
|
||||
parent_tl.attributes("-topmost", True)
|
||||
parent_tl.lift()
|
||||
parent_tl.focus_force()
|
||||
parent_tl.update_idletasks()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return func()
|
||||
finally:
|
||||
if parent_tl is not None:
|
||||
try:
|
||||
parent_tl.attributes("-topmost", False)
|
||||
except Exception:
|
||||
pass
|
||||
_restore_tool_topmost(parent_tl)
|
||||
_restore_main_topmost(main)
|
||||
|
||||
|
||||
def aza_showinfo(title, message, *, parent=None):
|
||||
from tkinter import messagebox
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: messagebox.showinfo(title, message, parent=parent),
|
||||
)
|
||||
|
||||
|
||||
def aza_showwarning(title, message, *, parent=None):
|
||||
from tkinter import messagebox
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: messagebox.showwarning(title, message, parent=parent),
|
||||
)
|
||||
|
||||
|
||||
def aza_showerror(title, message, *, parent=None):
|
||||
from tkinter import messagebox
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: messagebox.showerror(title, message, parent=parent),
|
||||
)
|
||||
|
||||
|
||||
def aza_askyesno(title, message, *, parent=None):
|
||||
from tkinter import messagebox
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: messagebox.askyesno(title, message, parent=parent),
|
||||
)
|
||||
|
||||
|
||||
def aza_askokcancel(title, message, *, parent=None):
|
||||
from tkinter import messagebox
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: messagebox.askokcancel(title, message, parent=parent),
|
||||
)
|
||||
|
||||
|
||||
def aza_askstring(title, prompt, *, parent=None, **kwargs):
|
||||
from tkinter import simpledialog
|
||||
return _run_with_visible_parent(
|
||||
parent, lambda: simpledialog.askstring(title, prompt, parent=parent, **kwargs),
|
||||
)
|
||||
|
||||
|
||||
def save_toplevel_geometry(window_name: str, geometry: str) -> None:
|
||||
"""Speichert die Geometrie eines Toplevel-Fensters."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user