Popups kommen nach Klick woanders / Alt+Tab wieder nach vorn; Büroklammer im Tipp des Tages
Fehler: War ein Popup offen und man klickte in ein anderes Programm, ließ sich das Popup über Hauptfenster, Taskleiste oder Alt+Tab nicht mehr nach vorn holen. Ursache: Rahmenlose Tk-Fenster mit transient() legt Tk unter Windows als Kindfenster des Desktops (WS_CHILD) an – ohne Besitzer, daher kam beim Zurückholen nur das (gesperrte) Hauptfenster nach vorn. - Popups (Dialoge, Rückfragen, Rundgang) ohne transient(), stattdessen Windows-Besitzer + Sperre des aufrufenden Fensters wie bei echten modalen Windows-Dialogen (gui/win32.py) - Rundgang: neue Sprechblase erscheint vor dem Entfernen der alten (sonst springt Windows weg) - Alt+F4 und Modalsperre robust gegen Timing (Sperre wird bei Bedarf nachgeholt) - Tests (nur unter Windows/Wine): Besitzer, Sperre, Stapelreihenfolge nach „woanders klicken“ und Zurückholen, verschachtelte Rückfragen, Rundgang - Tipp des Tages mit freigestelltem Büroklammer-Bild (build/make_tipp_bild.py), in der .exe enthalten und vom Selbsttest geprüft Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aae28fa7f2
commit
26ac90bb0f
10 files changed
+417
-29
No files matched your search
@@ -448,3 +448,113 @@ def test_hauptfenster_ablaeufe(lauf_mit_daten):
|
||||
assert not lauf.uebrig, f"nicht ausgeführt: {lauf.uebrig}"
|
||||
assert any(p["prozent"] == 80 for p in app.positionen)
|
||||
lauf.pruefen()
|
||||
|
||||
|
||||
# =========================================================================== Windows: Popups bleiben erreichbar
|
||||
|
||||
import sys # noqa: E402
|
||||
|
||||
nur_windows = pytest.mark.skipif(not sys.platform.startswith("win"),
|
||||
reason="Win32-Fensterbesitz gibt es nur unter Windows (im Build: Wine)")
|
||||
|
||||
|
||||
@nur_windows
|
||||
def test_windows_popup_kommt_nach_alt_tab_wieder_nach_vorn(lauf_mit_daten):
|
||||
"""Gemeldet: Popup offen, woanders hingeklickt – danach kam das Popup nicht mehr nach vorn
|
||||
(auch nicht per Alt+Tab). Geprüft wird: Popup gehört dem Hauptfenster, das Hauptfenster ist
|
||||
gesperrt, und holt man das Hauptfenster zurück, liegt das Popup darüber."""
|
||||
from provtool.gui import win32
|
||||
from provtool.gui.dialogs import InfoDialog
|
||||
lauf = lauf_mit_daten
|
||||
m = {}
|
||||
|
||||
def messen():
|
||||
d = lauf.dialog(InfoDialog)
|
||||
fremd = tk.Toplevel(lauf.root) # steht für ein anderes Programm
|
||||
fremd.geometry("400x300+50+50")
|
||||
fremd.update()
|
||||
m.update(besitzer=win32.besitzer_von(d), haupt=win32.hwnd(lauf.root),
|
||||
haupt_bedienbar=win32.ist_bedienbar(lauf.root))
|
||||
win32.nach_vorn(fremd) # „woanders hingeklickt“
|
||||
lauf.root.update()
|
||||
m["woanders"] = win32.stapel(d, lauf.root, fremd)
|
||||
win32.nach_vorn(lauf.root) # Alt+Tab / Taskleiste zurück zu uns
|
||||
lauf.root.update()
|
||||
m["zurueck"] = win32.stapel(d, lauf.root, fremd)
|
||||
fremd.destroy()
|
||||
d.abbrechen()
|
||||
|
||||
lauf.ablauf([
|
||||
(IMMER, lambda: InfoDialog(lauf.root).zeigen(), "Info öffnen"),
|
||||
(lambda: lauf.dialog(InfoDialog) is not None, lambda: lauf.root.after(150, messen), "messen"),
|
||||
(lambda: "zurueck" in m and lauf.dialog(InfoDialog) is None, lambda: None, "geschlossen"),
|
||||
], sekunden=10)
|
||||
assert not lauf.uebrig, lauf.uebrig
|
||||
assert m["besitzer"] == m["haupt"], "Popup gehört nicht dem Hauptfenster"
|
||||
assert m["haupt_bedienbar"] is False, "Hauptfenster muss gesperrt sein, solange das Popup offen ist"
|
||||
assert m["woanders"][0] == 2, f"Testaufbau: das fremde Fenster sollte oben liegen ({m['woanders']})"
|
||||
assert m["zurueck"][:2] == [0, 1], f"Popup liegt nach dem Zurückholen nicht über dem Hauptfenster: {m['zurueck']}"
|
||||
assert win32.ist_bedienbar(lauf.root), "Hauptfenster nach dem Schließen nicht wieder freigegeben"
|
||||
lauf.pruefen()
|
||||
|
||||
|
||||
@nur_windows
|
||||
def test_windows_verschachtelte_rueckfrage(lauf_mit_daten):
|
||||
"""Händler-Dialog → Rückfrage: Rückfrage gehört dem Händler-Dialog; beim Schließen der Reihe nach frei."""
|
||||
from provtool.gui import win32
|
||||
from provtool.gui import win95 as w
|
||||
from provtool.gui.dialogs import HaendlerDialog
|
||||
lauf = lauf_mit_daten
|
||||
m = {}
|
||||
rueckfrage = lambda: next((d for d in lauf.dialoge() if d.title() == "Test-Rückfrage"), None) # noqa: E731
|
||||
|
||||
def messen_innen():
|
||||
h, r = lauf.dialog(HaendlerDialog), rueckfrage()
|
||||
m.update(r_besitzer=win32.besitzer_von(r), h=win32.hwnd(h), h_an=win32.ist_bedienbar(h),
|
||||
haupt_an=win32.ist_bedienbar(lauf.root))
|
||||
r.destroy()
|
||||
|
||||
def messen_aussen():
|
||||
h = lauf.dialog(HaendlerDialog)
|
||||
m.update(h_an_danach=win32.ist_bedienbar(h), haupt_an_danach=win32.ist_bedienbar(lauf.root))
|
||||
h.abbrechen()
|
||||
|
||||
lauf.ablauf([
|
||||
(IMMER, lauf.app.haendler, "Händler-Dialog öffnen"),
|
||||
(lambda: lauf.dialog(HaendlerDialog) is not None,
|
||||
lambda: w.info(lauf.dialog(HaendlerDialog), "Test-Rückfrage", "?"), "Rückfrage öffnen"),
|
||||
(lambda: rueckfrage() is not None, lambda: lauf.root.after(100, messen_innen), "innen messen"),
|
||||
(lambda: "h" in m and rueckfrage() is None, messen_aussen, "außen messen"),
|
||||
(lambda: lauf.dialog(HaendlerDialog) is None, lambda: None, "alles zu"),
|
||||
], sekunden=12)
|
||||
assert not lauf.uebrig, lauf.uebrig
|
||||
assert m["r_besitzer"] == m["h"], "Rückfrage gehört nicht dem Händler-Dialog"
|
||||
assert m["h_an"] is False and m["haupt_an"] is False
|
||||
assert m["h_an_danach"] is True and m["haupt_an_danach"] is False
|
||||
assert win32.ist_bedienbar(lauf.root)
|
||||
lauf.pruefen()
|
||||
|
||||
|
||||
@nur_windows
|
||||
def test_windows_rundgang_sperrt_und_gibt_frei(lauf_mit_daten):
|
||||
from provtool.gui import win32
|
||||
lauf = lauf_mit_daten
|
||||
rg = lambda: getattr(lauf.app, "_rundgang", None) # noqa: E731
|
||||
m = {}
|
||||
|
||||
def messen_und_weiter():
|
||||
m.setdefault("gesperrt", []).append(not win32.ist_bedienbar(lauf.root))
|
||||
m.setdefault("besitzer", []).append(win32.besitzer_von(rg().blase) == win32.hwnd(lauf.root))
|
||||
rg()._weiter()
|
||||
|
||||
schritte = [(IMMER, lauf.app.rundgang, "Rundgang starten")]
|
||||
for i in range(3):
|
||||
schritte.append((lambda i=i: rg() is not None and rg().blase is not None and rg().nr == i,
|
||||
messen_und_weiter, f"Schritt {i + 1}"))
|
||||
schritte.append((IMMER, lambda: rg().beenden(), "Rundgang beenden"))
|
||||
lauf.ablauf(schritte, sekunden=12)
|
||||
assert not lauf.uebrig, lauf.uebrig
|
||||
assert all(m["gesperrt"]), "Hauptfenster während des Rundgangs nicht gesperrt"
|
||||
assert all(m["besitzer"]), "Sprechblase gehört nicht dem Hauptfenster"
|
||||
assert win32.ist_bedienbar(lauf.root), "Hauptfenster nach dem Rundgang nicht freigegeben"
|
||||
lauf.pruefen()
|
||||
Reference in new issue
Block a user