Anzeigefenster immer mit Rahmen, Pfad der Korrektur-Datei im Tab (#56)
Befund aus dem QGIS-Test: Die Bildschirmwahl wirkte nicht, das Fenster erschien immer auf dem Hauptbildschirm. Im Vollbild ließ es sich weder verschieben noch schließen und verdeckte QGIS; nur ein hartes Beenden von QGIS half. Bildschirmwahl und Vollbild entfallen. Das Anzeigefenster öffnet als normales Fenster mit Rahmen; der Bearbeiter schiebt es auf den Bildschirm des Eigentümers und kann es dort maximieren. Größe und Lage werden beim Beenden gemerkt und in der nächsten Sitzung wiederhergestellt (bis zum Neustart von QGIS). Unter den Knöpfen steht jetzt Pfad und Name der Korrektur-Datei, mit Hinweis, solange sie noch nicht angelegt ist oder erst abgefragt wird. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
Alle Namen und Anschriften sind erfunden.
|
||||
"""
|
||||
|
||||
import types
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@@ -15,10 +14,6 @@ ANNA = Eigentuemer(anrede="Frau", titel="Dr.", vorname="Anna", nachname="Beispie
|
||||
verstorben=True, rechtsperson=("0",), per_ids=("4711",), konten=("98765",))
|
||||
|
||||
|
||||
def _rechteck(x, y, b, h):
|
||||
return types.SimpleNamespace(x=lambda: x, y=lambda: y, width=lambda: b, height=lambda: h)
|
||||
|
||||
|
||||
class TestAnzeigetext(unittest.TestCase):
|
||||
|
||||
def test_name_anschrift_email(self):
|
||||
@@ -57,52 +52,39 @@ class TestAnzeigetext(unittest.TestCase):
|
||||
self.assertNotIn("Name", text)
|
||||
|
||||
|
||||
class TestBildschirme(unittest.TestCase):
|
||||
class TestFensterOeffnen(unittest.TestCase):
|
||||
|
||||
def test_vorgabe_ist_nicht_der_qgis_bildschirm(self):
|
||||
"""Prüft, dass als Vorgabe der erste Bildschirm ohne QGIS gewählt wird, bei nur einem Bildschirm dieser."""
|
||||
a, b, c = object(), object(), object()
|
||||
self.assertEqual(ea.standard_index([a, b, c], a), 1)
|
||||
self.assertEqual(ea.standard_index([a, b], b), 0)
|
||||
self.assertEqual(ea.standard_index([a], a), 0)
|
||||
self.assertEqual(ea.standard_index([], None), 0)
|
||||
|
||||
def test_bildschirm_text(self):
|
||||
"""Prüft, dass ein Bildschirm mit Nummer und Auflösung beschriftet wird."""
|
||||
bildschirm = types.SimpleNamespace(geometry=lambda: _rechteck(0, 0, 1920, 1080))
|
||||
self.assertEqual(ea.bildschirm_text(bildschirm, 2), "Bildschirm 2 (1920 × 1080)")
|
||||
self.assertEqual(ea.bildschirm_text(object(), 3), "Bildschirm 3")
|
||||
|
||||
def test_ohne_qt_anwendung_keine_bildschirme(self):
|
||||
"""Prüft, dass ohne Qt-Anwendung (Mock-Modus) eine leere Bildschirmliste kommt statt eines Fehlers."""
|
||||
self.assertEqual(ea.bildschirme(), [])
|
||||
|
||||
def test_vollbild_auf_gewaehltem_bildschirm(self):
|
||||
"""Prüft, dass das Fenster im Vollbild dem gewählten Bildschirm zugeordnet und auf dessen Fläche gelegt wird."""
|
||||
geo = _rechteck(1920, 0, 1920, 1080)
|
||||
bildschirm = types.SimpleNamespace(geometry=lambda: geo)
|
||||
def test_normales_fenster_in_standardgroesse(self):
|
||||
"""Prüft, dass das Anzeigefenster ohne gemerkte Lage als normales Fenster mit Rahmen in Standardgröße öffnet, nie im Vollbild."""
|
||||
fenster = MagicMock()
|
||||
ea.zeige_auf_bildschirm(fenster, bildschirm, vollbild=True)
|
||||
fenster.windowHandle.return_value.setScreen.assert_called_once_with(bildschirm)
|
||||
fenster.setGeometry.assert_called_once_with(geo)
|
||||
fenster.showFullScreen.assert_called_once()
|
||||
fenster.showNormal.assert_not_called()
|
||||
|
||||
def test_normales_fenster_mittig(self):
|
||||
"""Prüft, dass auf dem QGIS-Bildschirm ein normales Fenster mittig im freien Bereich öffnet."""
|
||||
bildschirm = types.SimpleNamespace(availableGeometry=lambda: _rechteck(0, 40, 1600, 1000))
|
||||
fenster = MagicMock()
|
||||
ea.zeige_auf_bildschirm(fenster, bildschirm, vollbild=False)
|
||||
fenster.setGeometry.assert_called_once_with(350, 215, 900, 650)
|
||||
fenster.showNormal.assert_called_once()
|
||||
fenster.showFullScreen.assert_not_called()
|
||||
|
||||
def test_ohne_bildschirm_einfach_zeigen(self):
|
||||
"""Prüft, dass ohne bekannten Bildschirm das Fenster einfach gezeigt wird."""
|
||||
fenster = MagicMock()
|
||||
ea.zeige_auf_bildschirm(fenster, None, vollbild=True)
|
||||
ea.zeige_anzeigefenster(fenster)
|
||||
fenster.resize.assert_called_once_with(*ea.STANDARDGROESSE)
|
||||
fenster.show.assert_called_once()
|
||||
fenster.showFullScreen.assert_not_called()
|
||||
fenster.setWindowFlags.assert_not_called()
|
||||
|
||||
def test_gemerkte_lage_wird_wiederhergestellt(self):
|
||||
"""Prüft, dass eine gemerkte Größe und Lage der letzten Sitzung wiederhergestellt wird."""
|
||||
fenster = MagicMock()
|
||||
fenster.restoreGeometry.return_value = True
|
||||
ea.zeige_anzeigefenster(fenster, b"geometrie")
|
||||
fenster.restoreGeometry.assert_called_once_with(b"geometrie")
|
||||
fenster.resize.assert_not_called()
|
||||
fenster.show.assert_called_once()
|
||||
fenster.showFullScreen.assert_not_called()
|
||||
|
||||
def test_unbrauchbare_lage_ergibt_standardgroesse(self):
|
||||
"""Prüft, dass eine nicht wiederherstellbare Lage auf die Standardgröße zurückfällt."""
|
||||
for ergebnis in (False, RuntimeError("kaputt")):
|
||||
with self.subTest(ergebnis=ergebnis):
|
||||
fenster = MagicMock()
|
||||
if isinstance(ergebnis, Exception):
|
||||
fenster.restoreGeometry.side_effect = ergebnis
|
||||
else:
|
||||
fenster.restoreGeometry.return_value = ergebnis
|
||||
ea.zeige_anzeigefenster(fenster, b"alt")
|
||||
fenster.resize.assert_called_once_with(*ea.STANDARDGROESSE)
|
||||
fenster.show.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -192,9 +192,12 @@ class TestTrefferliste(unittest.TestCase):
|
||||
|
||||
def _anz_tab(angezeigt=None, anzeige=None, layer_id="csv1"):
|
||||
tab = EigentuemerTab.__new__(EigentuemerTab)
|
||||
for name in ("btn_sitzung", "btn_anzeige_leeren", "btn_korrigieren", "sitzung_label", "bildschirm_combo"):
|
||||
for name in ("btn_sitzung", "btn_anzeige_leeren", "btn_korrigieren", "sitzung_label", "korrektur_label",
|
||||
"eigentuemer_layer_combo"):
|
||||
setattr(tab, name, MagicMock())
|
||||
tab.eigentuemer_layer_combo.currentData.return_value = layer_id
|
||||
tab._anzeige = anzeige
|
||||
tab._anzeige_geometrie = None
|
||||
tab._sitzung_seit = datetime(2026, 10, 6, 9, 5) if anzeige is not None else None
|
||||
tab._angezeigt = list(angezeigt or [])
|
||||
tab._treffer_layer_id = layer_id
|
||||
@@ -206,35 +209,32 @@ def _anz_tab(angezeigt=None, anzeige=None, layer_id="csv1"):
|
||||
_BERND = Eigentuemer(vorname="Bernd", nachname="Muster", konten=("300",), per_ids=("8",))
|
||||
|
||||
|
||||
@patch.object(et, "zeige_auf_bildschirm")
|
||||
@patch.object(et, "qgis_bildschirm")
|
||||
@patch.object(et, "hole_layer", return_value=None)
|
||||
@patch.object(et, "zeige_anzeigefenster")
|
||||
@patch.object(et, "EigentuemerAnzeige")
|
||||
class TestSitzung(unittest.TestCase):
|
||||
|
||||
def test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm(self, anzeige_klasse, qgis, zeige):
|
||||
"""Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster auf dem gewählten Bildschirm öffnet, im Vollbild, wenn QGIS woanders liegt."""
|
||||
def test_sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster als normales Fenster öffnet (ohne gemerkte Lage beim ersten Mal)."""
|
||||
tab = _anz_tab()
|
||||
zweiter = object()
|
||||
tab.bildschirm_combo.gewaehlter_bildschirm.return_value = zweiter
|
||||
qgis.return_value = object()
|
||||
tab._on_sitzung_umschalten()
|
||||
anzeige_klasse.assert_called_once_with(tab)
|
||||
zeige.assert_called_once_with(anzeige_klasse.return_value, zweiter, True)
|
||||
zeige.assert_called_once_with(anzeige_klasse.return_value, None)
|
||||
self.assertIs(tab._anzeige, anzeige_klasse.return_value)
|
||||
self.assertEqual(tab._angezeigt, [])
|
||||
tab.btn_sitzung.setText.assert_called_with("Sitzung beenden")
|
||||
self.assertIn("leer", tab.sitzung_label.setText.call_args[0][0])
|
||||
|
||||
def test_auf_qgis_bildschirm_kein_vollbild(self, anzeige_klasse, qgis, zeige):
|
||||
"""Prüft, dass das Anzeigefenster auf dem QGIS-Bildschirm als normales Fenster öffnet."""
|
||||
def test_lage_gilt_fuer_naechste_sitzung(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass Größe und Lage des Fensters beim Beenden gemerkt und in der nächsten Sitzung wiederverwendet werden."""
|
||||
tab = _anz_tab()
|
||||
einziger = object()
|
||||
tab.bildschirm_combo.gewaehlter_bildschirm.return_value = einziger
|
||||
qgis.return_value = einziger
|
||||
tab._sitzung_beginnen()
|
||||
self.assertFalse(zeige.call_args[0][2])
|
||||
anzeige_klasse.return_value.saveGeometry.return_value = b"zweiter-bildschirm"
|
||||
tab._sitzung_beenden()
|
||||
tab._sitzung_beginnen()
|
||||
self.assertEqual(zeige.call_args_list[-1][0][1], b"zweiter-bildschirm")
|
||||
|
||||
def test_daten_anzeigen_beginnt_sitzung_bei_bedarf(self, anzeige_klasse, qgis, zeige):
|
||||
def test_daten_anzeigen_beginnt_sitzung_bei_bedarf(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl."""
|
||||
tab = _anz_tab()
|
||||
tab._daten_anzeigen([_ANNA, _BERND])
|
||||
@@ -244,7 +244,7 @@ class TestSitzung(unittest.TestCase):
|
||||
self.assertIn("2 Einträge", status)
|
||||
self.assertNotIn("Beispiel", status)
|
||||
|
||||
def test_daten_anzeigen_ohne_auswahl(self, anzeige_klasse, qgis, zeige):
|
||||
def test_daten_anzeigen_ohne_auswahl(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt."""
|
||||
tab = _anz_tab()
|
||||
with patch.object(et, "warning") as warnung:
|
||||
@@ -252,7 +252,7 @@ class TestSitzung(unittest.TestCase):
|
||||
warnung.assert_called_once()
|
||||
anzeige_klasse.assert_not_called()
|
||||
|
||||
def test_anzeige_leeren_laesst_fenster_offen(self, anzeige_klasse, qgis, zeige):
|
||||
def test_anzeige_leeren_laesst_fenster_offen(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt."""
|
||||
anzeige = MagicMock()
|
||||
tab = _anz_tab(angezeigt=[_ANNA], anzeige=anzeige)
|
||||
@@ -263,7 +263,7 @@ class TestSitzung(unittest.TestCase):
|
||||
self.assertIs(tab._anzeige, anzeige)
|
||||
tab.btn_anzeige_leeren.setEnabled.assert_called_with(False)
|
||||
|
||||
def test_sitzung_beenden_schliesst_fenster(self, anzeige_klasse, qgis, zeige):
|
||||
def test_sitzung_beenden_schliesst_fenster(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst."""
|
||||
anzeige = MagicMock()
|
||||
tab = _anz_tab(angezeigt=[_ANNA], anzeige=anzeige)
|
||||
@@ -274,21 +274,64 @@ class TestSitzung(unittest.TestCase):
|
||||
self.assertEqual(tab._angezeigt, [])
|
||||
tab.btn_sitzung.setText.assert_called_with("Sitzung beginnen")
|
||||
|
||||
def test_von_aussen_geschlossen_beendet_sitzung(self, anzeige_klasse, qgis, zeige):
|
||||
"""Prüft, dass ein von außen geschlossenes Anzeigefenster (z. B. Alt+F4) die Sitzung im Tab beendet."""
|
||||
def test_von_aussen_geschlossen_beendet_sitzung(self, anzeige_klasse, zeige, _layer):
|
||||
"""Prüft, dass ein über den Fensterrahmen geschlossenes Anzeigefenster die Sitzung beendet und seine Lage gemerkt wird."""
|
||||
tab = _anz_tab()
|
||||
tab._sitzung_beginnen()
|
||||
rueckruf = anzeige_klasse.return_value.beim_schliessen
|
||||
rueckruf()
|
||||
anzeige_klasse.return_value.saveGeometry.return_value = b"lage"
|
||||
anzeige_klasse.return_value.beim_schliessen()
|
||||
self.assertIsNone(tab._anzeige)
|
||||
self.assertEqual(tab._anzeige_geometrie, b"lage")
|
||||
|
||||
|
||||
class TestKorrekturLabel(unittest.TestCase):
|
||||
|
||||
def _text(self, tab):
|
||||
return tab.korrektur_label.setText.call_args[0][0]
|
||||
|
||||
def test_ohne_eigentuemerdatei(self):
|
||||
"""Prüft, dass ohne Eigentümerdatei unter den Knöpfen steht, dass keine gewählt ist."""
|
||||
tab = _anz_tab(layer_id=None)
|
||||
tab._aktualisiere_korrektur_label()
|
||||
self.assertIn("keine Eigentümerdatei", self._text(tab))
|
||||
|
||||
def test_pfad_neben_der_eigentuemerdatei(self):
|
||||
"""Prüft, dass der Pfad der Korrektur-CSV angezeigt wird, mit Hinweis, solange sie noch nicht angelegt ist."""
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
quelle = "file:///" + os.path.join(ordner, "export.csv").replace(os.sep, "/") + "?type=csv"
|
||||
erwartet = os.path.normpath(os.path.join(ordner, "export_korrekturen.csv"))
|
||||
tab = _anz_tab()
|
||||
with patch.object(et, "hole_layer", return_value=types.SimpleNamespace(source=lambda: quelle)):
|
||||
tab._aktualisiere_korrektur_label()
|
||||
self.assertEqual(os.path.normcase(self._text(tab)),
|
||||
os.path.normcase(f"Korrektur-Datei: {erwartet} (wird beim ersten Speichern angelegt)"))
|
||||
open(erwartet, "w").close()
|
||||
tab._aktualisiere_korrektur_label()
|
||||
self.assertEqual(os.path.normcase(self._text(tab)), os.path.normcase(f"Korrektur-Datei: {erwartet}"))
|
||||
|
||||
def test_ohne_csv_quelle_wird_abgefragt(self):
|
||||
"""Prüft, dass bei einer Quelle ohne CSV angekündigt wird, dass der Speicherort beim ersten Speichern abgefragt wird."""
|
||||
tab = _anz_tab()
|
||||
with patch.object(et, "hole_layer", return_value=types.SimpleNamespace(source=lambda: "memory?geometry=None")):
|
||||
tab._aktualisiere_korrektur_label()
|
||||
self.assertIn("abgefragt", self._text(tab))
|
||||
|
||||
def test_gewaehlter_pfad_wird_angezeigt(self):
|
||||
"""Prüft, dass ein einmal gewählter Speicherort angezeigt wird."""
|
||||
tab = _anz_tab()
|
||||
tab._korrektur_pfade["csv1"] = "D:/Termine/korrekturen.csv"
|
||||
with patch.object(et, "hole_layer", return_value=None):
|
||||
tab._aktualisiere_korrektur_label()
|
||||
self.assertIn(os.path.normpath("D:/Termine/korrekturen.csv"), self._text(tab))
|
||||
|
||||
|
||||
@patch.object(et, "hole_layer", return_value=None)
|
||||
@patch.object(et, "success")
|
||||
@patch.object(et, "info")
|
||||
@patch.object(et, "warning")
|
||||
class TestKorrektur(unittest.TestCase):
|
||||
|
||||
def test_korrektur_wird_festgehalten_und_angezeigt(self, warnung, hinweis, erfolg):
|
||||
def test_korrektur_wird_festgehalten_und_angezeigt(self, warnung, hinweis, erfolg, _layer):
|
||||
"""Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird."""
|
||||
anzeige = MagicMock()
|
||||
tab = _anz_tab(angezeigt=[_ANNA, _BERND], anzeige=anzeige)
|
||||
@@ -305,7 +348,7 @@ class TestKorrektur(unittest.TestCase):
|
||||
self.assertIn("export_korrekturen.csv", meldung)
|
||||
self.assertNotIn("Muster", meldung)
|
||||
|
||||
def test_ohne_aenderung_nichts_schreiben(self, warnung, hinweis, erfolg):
|
||||
def test_ohne_aenderung_nichts_schreiben(self, warnung, hinweis, erfolg, _layer):
|
||||
"""Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt."""
|
||||
tab = _anz_tab(angezeigt=[_ANNA], anzeige=MagicMock())
|
||||
with patch.object(et, "frage_korrektur", return_value=(0, {"nachname": "Beispiel"})), \
|
||||
@@ -314,7 +357,7 @@ class TestKorrektur(unittest.TestCase):
|
||||
schreiben.assert_not_called()
|
||||
hinweis.assert_called_once()
|
||||
|
||||
def test_abbruch_nichts_schreiben(self, warnung, hinweis, erfolg):
|
||||
def test_abbruch_nichts_schreiben(self, warnung, hinweis, erfolg, _layer):
|
||||
"""Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt."""
|
||||
tab = _anz_tab(angezeigt=[_ANNA], anzeige=MagicMock())
|
||||
with patch.object(et, "frage_korrektur", return_value=None), \
|
||||
@@ -322,7 +365,7 @@ class TestKorrektur(unittest.TestCase):
|
||||
tab._on_korrigieren()
|
||||
schreiben.assert_not_called()
|
||||
|
||||
def test_schreibfehler_warnung_und_anzeige_unveraendert(self, warnung, hinweis, erfolg):
|
||||
def test_schreibfehler_warnung_und_anzeige_unveraendert(self, warnung, hinweis, erfolg, _layer):
|
||||
"""Prüft, dass bei einem Schreibfehler gewarnt, die Anzeige nicht geändert und der Pfad beim nächsten Mal neu bestimmt wird."""
|
||||
anzeige = MagicMock()
|
||||
tab = _anz_tab(angezeigt=[_ANNA], anzeige=anzeige)
|
||||
@@ -335,7 +378,7 @@ class TestKorrektur(unittest.TestCase):
|
||||
self.assertIs(tab._angezeigt[0], _ANNA)
|
||||
self.assertNotIn("csv1", tab._korrektur_pfade)
|
||||
|
||||
def test_ohne_anzeige_keine_korrektur(self, warnung, hinweis, erfolg):
|
||||
def test_ohne_anzeige_keine_korrektur(self, warnung, hinweis, erfolg, _layer):
|
||||
"""Prüft, dass ohne angezeigte Daten kein Korrekturdialog erscheint."""
|
||||
tab = _anz_tab()
|
||||
with patch.object(et, "frage_korrektur") as dialog:
|
||||
|
||||
Reference in New Issue
Block a user