Skip to content

Statusspalte nur so breit wie ihr Schild - #18

Merged
oe5sos merged 17 commits into
mainfrom
fix/statusspalte-schmal
Sep 30, 2026
Merged

oe5sos merged 17 commits into
mainfrom
fix/statusspalte-schmal

Conversation

@oe5sos

@oe5sos oe5sos commented Sep 29, 2026

Copy link
Copy Markdown
Owner

Martin, 2026-09-29: „bitte viel schmäler machen, ich benötige platz!!!" — über die letzte Spalte im Log. Sie wuchs bis 110 px in jede freie Lücke und stand auf fast jeder Zeile leer da.

Jetzt wird gemessen statt geschätzt: die Breite kommt aus der Schrift, in der PillDelegate die Schilder zeichnet (UNGÜLTIG, DUPE, KST, CLU), mit demselben Innenabstand und Einzug. 71 statt 110 px — und es bleibt richtig, wenn sich die Schriftgröße ändert.

Einen Zwischenschritt habe ich wieder verworfen: den frei gewordenen Platz an die Rufzeichenspalte zu geben. Die wuchs damit auf 205 px — derselbe breite Block, nur woanders. Jetzt dehnt sich keine Spalte mehr.

Den Folgefehler fand die Suite: der Prüfstand für schmale Panels sah, dass die Eingabezeile bei der gewachsenen Spalte nicht mitging.

107/107 grün.

🤖 Generated with Claude Code

oe5sos and others added 17 commits September 29, 2026 06:23
Martin, 2026-09-29, mit Bild: "im chatroom 144 sind
kurzwelleneinträge, bitte kontrolliere ob dieser chat auch wirklich
den raum ändert."

Zwei getrennte Dinge.

**Der Raum.** Gesetzt wird er beim Login (LOGINC|...|chat_id|,
bestätigt mit LOGSTAT|100|chat_id|). switchRoom() schickt dagegen
"/CHAT 144" -- das steht so in der wtKST-Doku, die dieser Client
selbst als "not verified live" kennzeichnet, und ist bis heute nicht
nachgeprüft. Statt weiter zu glauben, wird jetzt gemessen: On4kstClient
liest die chat_id aus jeder eingehenden CH|/CR|-Zeile und meldet sie
als roomObserved(). Im Chatkopf steht beides nebeneinander -- welcher
Raum angefordert wurde und aus welchem die Zeilen kommen. Weichen sie
ab, war der Wechsel wirkungslos, und man sieht es sofort. Live zeigt
es derzeit: "Raum 144 angefordert, Zeilen kommen aus Raum 2" -- Raum 2
ist der 144/432-Raum, das passt, gesetzt hat ihn das Login.

**Die Kurzwelle.** Die Zeilen im Bild trugen CLU, nicht KST: sie kamen
vom DX-Cluster, der weltweit alles spottet, auch 1,8 und 7 MHz. In
einem UKW-Contest kann einen das nicht erreichen -- genau der Fall,
den der Filter abdecken soll. Spots, deren Band der aktive Contest
nicht kennt, bleiben jetzt draußen. Eine Chatzeile OHNE Frequenz
bleibt (über die sagt die Frequenz nichts), ein unbekanntes Band
ebenso, und unter "alle Zeilen zeigen" ist alles weiter einsehbar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "bitte kontrolliere ob dieser chat auch wirklich
den raum ändert." Nein, tat er nicht: switchRoom() schickte
"/CHAT 144" aus einer Doku, die dieser Client selbst als ungeprüft
führt. Einen Raum setzt bei ON4KST nur das Login (LOGINC|...|chat_id|,
bestätigt mit LOGSTAT|100|chat_id|).

Also meldet sich der Client jetzt mit der gewünschten Raumnummer neu
an. Am Prüfstand belegt: nach switchRoom(3) kommt beim Server
"LOGINC|...|3|" an, die folgenden Zeilen tragen Raum 3, und
roomObserved() meldet ihn. Derselbe Raum noch einmal löst KEIN
zweites Login aus, sonst risse jeder Bandabgleich die Verbindung ohne
Not ab.

Dabei zwei Fallen:

- Ein connectToHost() auf einem Socket, der gerade zumacht, wird
  stillschweigend verworfen -- der Socket ging brav in
  ConnectingState, beim Server kam aber nie etwas an. Der Neuaufbau
  hängt jetzt am disconnected-Signal, und abort() steht mit im
  verzögerten Teil.

- Der PRÜFSTAND selbst war kaputt: der Mock-Server schrieb alles, was
  nach dem ersten CK| ankam, nur noch in einen Puffer und parste es
  nicht mehr. Ein zweites LOGINC sah er nie. Er hätte jeden
  Raumwechsel für kaputt erklärt, egal wie richtig der Code ist.

Das Menü führt die Räume jetzt unter ihrer chat_id (1 = 50/70,
2 = 144/432, 3 = Mikrowelle, 4 = EME, 5 = Kurzwelle). Belegt ist
davon nur die 2; ob eine andere Nummer den erwarteten Raum trifft,
zeigt nach dem Wechsel der Chatkopf.

Dazu eine Mitschrift für die Fehlersuche: CP_KST_MITSCHRIFT=<pfad>
schreibt jede Protokollzeile roh mit. Ohne die Variable passiert
nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Live erlebt am 2026-09-29: nach etlichen Neustarts blieb die
Statusleiste grau und der Chat leer -- bis zum nächsten
Programmstart. Der Grund stand im Code: JEDER Login-Fehlercode außer
100 galt als endgültig ("stop hammering it"). Gedacht war das gegen
falsche Zugangsdaten, es trifft aber genauso den vorübergehenden
Fall: Sitzung noch offen, Server überlastet, Netz kurz weg. Mitten im
Contest ist ein stiller Chat schlimmer als ein Versuch alle paar
Minuten.

Welcher Code was bedeutet, ist nicht belegt (nur 100 = Erfolg), also
wird nicht geraten, sondern langsam weiterprobiert: erster neuer
Anlauf nach einer Minute, dann verdoppelnd bis zehn. Kein Hämmern,
und der Chat kommt von allein wieder, sobald der Grund weg ist.

Prüfstand mit Gegenprobe: der Mock weist den ersten Login ab, danach
muss ein neuer Anlauf anstehen; mit der alten Fassung steht keiner an
und der Test ist rot.

Dabei ein eigener Fehler im Prüfstand: allein gelaufen war er grün,
in der Reihe rot. Der neue Anlauf wird erst gestellt, wenn die
Leitung wirklich unten ist -- sofort danach zu prüfen war ein
Zeitfehler im Prüfstand, nicht im Programm. Jetzt wartet er darauf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29, mit Bild: "kann nicht anklicken". Das ganze
Raummenü hing an "angemeldet", und angemeldet war er gerade nicht:
ON4KST war an dem Morgen schlicht nicht erreichbar (aus seiner
eigenen Shell: "connectx to www.on4kst.org port 23001 failed:
Operation timed out"). Gerade dann will man den Raum wählen können.

Jetzt gilt die Wahl für den nächsten Anlauf -- und der wird sofort
genommen. Dazu ein Eintrag "Jetzt neu verbinden" im selben Menü,
ebenfalls immer anklickbar: bei einem stillen Chat muss man nicht
mehr das Programm neu starten. Away / Wieder da / CQ bleiben
gesperrt, solange keine Verbindung steht; die brauchen sie wirklich.

Der Prüfstand für das Menü sah bisher nur nach, dass die Einträge DA
sind. Genau das war die Lücke -- sie waren da und ließen sich nicht
anklicken. Er prüft jetzt beides. Dazu ein Prüfstand für den Weg
selbst: getrennt Raum 3 gewählt, und der Server sieht einen Login für
Raum 3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "bitte viel schmäler machen, ich benötige
platz!!!" -- über die letzte Spalte im Log. Sie wuchs bis 110 px in
jede freie Lücke und stand auf fast jeder Zeile leer da.

Jetzt wird gemessen statt geschätzt: die Breite kommt aus der
Schrift, in der PillDelegate die Schilder zeichnet (UNGÜLTIG, DUPE,
KST, CLU), mit demselben Innenabstand und Einzug. Das sind 71 statt
110 px, und es bleibt richtig, wenn sich die Schriftgröße ändert.

Einen Zwischenschritt habe ich wieder verworfen: den frei gewordenen
Platz an die Rufzeichenspalte zu geben, damit rechts kein Rand
entsteht. Die wuchs damit auf 205 px -- derselbe breite Block, nur
woanders. Jetzt dehnt sich keine Spalte mehr.

Gefunden hat den Folgefehler die Suite, nicht ich: der Prüfstand für
schmale Panels sah, dass die Eingabezeile bei der gewachsenen Spalte
nicht mitging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "dachte du hattest alles schon mehrmals
getestet" -- und er hat recht. Dreimal am selben Tag dasselbe Muster:
beim Herausziehen aus dem Seitenbereich (Menü ging, Ziehen nicht),
beim Chat-Zahnrad (Menü da, Knopf fehlte) und jetzt beim Log. Die
Prüfstände riefen jeweils die Funktion auf und bewiesen damit nur die
halbe Strecke.

Der Prüfstand für die Korrektur klickt jetzt doppelt auf die Zelle
und sieht nach, ob ein Editor aufgeht. Dabei die eigentliche Lehre:
QTest::mouseDClick geht über das Fenstersystem und VERPUFFT, solange
das Fenster nicht wirklich auf einem Schirm liegt -- im Prüfstandslauf
nie. Die erste Fassung meldete deshalb "kein Editor", und ich hätte
beinahe einen Fehler gesucht, den es nicht gibt: clicked feuerte nicht
einmal. Mit QMouseEvent + sendEvent kommt an, was ankommen soll --
dieselbe Technik, die beim Seitenbereich den Zug am Panelkopf
nachgewiesen hat.

Ergebnis: das Ändern per Doppelklick funktioniert (clicked 1,
doubleClicked 1, Editor 1). Offen bleibt, dass nur Rufzeichen,
Nr./Locator und Zeit überhaupt editierbar sind.

Dazu eine Mitschrift für die Fehlersuche am Log
(CP_LOG_MITSCHRIFT=<pfad>), die festhält, was beim Klicken wirklich
ankommt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "beim contest kann ich mir keine fehler leisten"
und "da muss einfach und alles perfekt laufen".

Korrektur von Rufzeichen und Locator laufen nicht mehr über
setData(), sondern wie im Contest: doppelt auf die Zelle, tippen,
Enter -- und bei JEDEM dieser Schritte wird geprüft, dass der Editor
überhaupt aufgeht. Dazu ein neuer Handgriff: Klick auf die
Statuszelle, ungültig und wieder zurück.

Die Mausereignisse gehen per sendEvent direkt an den Viewport.
QTest::mouseClick geht über das Fenstersystem und verpufft, solange
das Fenster nicht wirklich auf einem Schirm liegt -- im
Prüfstandslauf nie. Jeder bisherige Maus-Prüfstand war damit blind.

Die Länge je Folge ist einstellbar (CP_RUETTELN_SCHRITTE), Vorgabe
200 wie bisher. Ein Contest dauert 24 Stunden; was dabei schiefgeht,
zeigt sich nicht in 200 Handgriffen.

Belegt:
- die ganze Suite unter AddressSanitizer + UBSan: 107/107, keine
  Zugriffe auf Gelöschtes, keine Überläufe, kein undefiniertes
  Verhalten
- Dauerlauf unter denselben Prüfern: 5 Folgen x 2000 Handgriffen =
  10.000 Schritte, 7/7 grün, am Ende 364 QSOs im Log

Dazu build-asan/ in .gitignore: ohne die Zeile sammelt ein
"git add -A" 4194 Objektdateien ein, und GitHub weist den Push ab --
genau so passiert, bevor dieser Commit stand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "primäres ziel und das muss zu 100 % passen sind
meine logs, die ich eingebe. die dürfen nicht weg sein."

Die Datenbank lief auf PRAGMA synchronous = NORMAL. Das schützt bei
einem Programmabsturz (dafür ist WAL da), aber NICHT bei Stromausfall
oder einem stehenden Rechner: dann kann die zuletzt bestätigte
Transaktion fehlen -- also genau das QSO, das man gerade geloggt hat.
Jetzt FULL.

Der neue Prüfstand test_log_haltbarkeit beweist es auf die harte
Tour. Ein eigener Prozess schreibt QSOs und wird mit SIGKILL
erschlagen: kein Beenden, kein Aufräumen, nichts wird nachträglich
weggeschrieben.

- 25 von 25 QSOs überleben den Abschuss
- Abschuss MITTEN im Schreiben: alle bestätigten QSOs sind da
- danach lässt sich weiterloggen
- PRAGMA synchronous aus der laufenden Datei ausgelesen: 2 (FULL)
- der Preis, gemessen statt behauptet: 0,08 ms je QSO

Ein erster Versuch mit fork() endete mit SIGABRT -- die
Datenbankschicht verträgt das Abspalten mitten im Betrieb nicht. Ein
sauber gestarteter zweiter Prozess schon.

Dazu der Chat-Dauerlauf (12 Abbrüche, jedes Mal kommt der Client
zurück) und ein Fehler darin, den ich beinahe als Programmfehler
gemeldet hätte: nach einer Login-Ablehnung wartet der Client
ABSICHTLICH eine Minute, mein Prüfstand gab ihm acht Sekunden.

108/108 grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #17 (Chatraum, Kurzwellenfilter) ist inzwischen auf main, und
dieser Zweig hatte an denselben Stellen gearbeitet -- GitHub meldete
CONFLICTING und startete deshalb gar keine Prüfung.

Zusammengeführt statt eine Seite wegzuwerfen: der Mock kann jetzt
beides, einen Login abweisen (aus #17) und die Leitung kappen (von
hier). test_on4kstprotocol läuft mit allen zwölf Fällen beider
Zweige, die volle Suite mit 108/108.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-29: "die dürfen nicht weg sein."

Die Datenbank ist seit heute gegen Stromausfall gesichert. Sie kann
aber auf andere Weise versagen: Platte voll, Datei gesperrt, Schema
kaputt, ein Fehler in diesem Programm. Dann stand das QSO in der
Eingabezeile und sonst nirgends -- mit einem Warnhinweis, aber ohne
Ausweg.

Jetzt hängt QsoJournal jedes QSO als ADIF-Zeile an eine Textdatei
neben der Datenbank an (<name>-journal.adi), mit flush und fsync, und
zwar BEVOR die Datenbank es zu sehen bekommt. Die Reihenfolge ist der
ganze Sinn.

Das Journal ist absichtlich das dümmste denkbare Verfahren: kein
Schema, keine Transaktion, kein Index, nichts, was kaputtgehen kann.
Eine Zeile, angehängt, auf Platte. Und weil es gültiges ADIF ist,
lässt es sich überall wieder einlesen.

Belegt im Prüfstand: bei schreibgeschützter Datenbank steht in der
Datenbank nichts ("false") und im Journal alles ("true"). Fünf QSOs,
fünf <EOR>-Zeilen, Rufzeichen und Locator drin.

Dazu toAdifRecord() aus dem anonymen Namensraum geholt -- dieselbe
Zeile, dasselbe Format wie der Export, keine zweite Rechnung daneben.

108/108 grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Journal, das man nicht zurückspielen kann, ist keine Sicherung.
Der ADIF-Leser überging bis jetzt Seriennummern und Rapporte: er war
für die Rufzeichen-/Locator-Kartei gebaut, nicht zum Wiederherstellen
eines Contest-Logs. Damit wäre ein zurückgeholtes Log ohne Punkte
gewesen.

Jetzt liest er STX, SRX, RST_SENT, RST_RCVD und CONTEST_ID mit, und
unter Datei > Sicherung steht "Aus dem Journal wiederherstellen...".
Es ERGÄNZT nur und löscht nie etwas -- wer das braucht, hat schon
genug verloren. Schon vorhandene QSOs (Rufzeichen + Band + Minute)
werden übersprungen.

Prüfstände, den ganzen Weg von hinten aufgezäumt:
- Journal schreiben und vollständig zurücklesen: Rufzeichen, Band,
  Modus, Locator, RST 59/59, Nr. 1/1, Contest-Kennung, Zeit
- Datenbank gelöscht, aus dem Journal wiederhergestellt: 12 von 12
  QSOs zurück, alle mit beiden Nummern

108/108 grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-30: "schreibe es in die liste für später mal zu
testen." Drei Teile: was nur er am Gerät prüfen kann, was gebaut aber
noch nicht am echten Gerät nachgefahren ist, und was noch zu bauen
ist. Dazu die zwei Fallen, die diese Woche schon zugeschlagen haben.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Martin, 2026-09-30: "bitte version immer in die taskleiste legen,
automatisch."

Links in der Statusleiste steht jetzt "v0.1.1", im Tooltip die vollen
Angaben: "Fassung 0.1.1 · Stand 2026-09-30 · d7ab9c7+". Das "+"
sagt, dass beim Bauen noch ungespeicherte Änderungen im Arbeitsbaum
lagen.

Automatisch heißt wörtlich: die Werte kommen aus BuildInfo.h, das
cmake/BuildInfo.cmake bei JEDEM Bau neu schreibt. Nichts von Hand zu
pflegen, und keine Fassung kann behaupten, eine andere zu sein.

Commit und Baudatum stehen bewusst nur im Tooltip -- auf der Leiste
kosteten sie Platz, den die Verbindungsanzeigen brauchen; beim Melden
eines Fehlers sind sie aber genau die Angabe, die zählt.

Prüfstand vergleicht die angezeigte Fassung mit der gebauten, damit
die Anzeige nicht stillschweigend veraltet. 108/108 grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Windows-CI brach schon beim Bauen ab. QsoJournal band <unistd.h>
ein und rief fsync() -- beides POSIX, unter Windows nicht vorhanden.
Dasselbe leistet dort _commit() auf demselben Dateideskriptor
(<io.h>). Jetzt steht beides unter #ifdef.

Dazu der neue Prüfstand für ein Journal, das nicht schreiben kann
(gesperrter Ordner): es MUSS das melden -- still zu scheitern wäre
das Schlimmste, man verließe sich auf ein Netz, das es nicht gibt --
und das Loggen in die Datenbank muss weiterlaufen. Ein kaputtes
Journal darf den Contest nicht anhalten. Belegt: "geschrieben: false
| Fehler: Permission denied", Datenbank nimmt das QSO trotzdem an.

Unter Windows wird dieser eine Fall übersprungen: setPermissions()
greift dort auf Ordnern nicht, das Schreiben gelänge trotzdem und der
Prüfstand wäre rot, ohne dass etwas falsch wäre. Lieber ehrlich
überspringen als eine Zusage prüfen, die dort nicht herstellbar ist.

Lokal 108/108 grün. Ob der Windows-Bau jetzt durchgeht, sagt die CI --
prüfen kann ich es hier nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zweiter Windows-Fehler in Folge, diesmal beim Linken: "unresolved
external symbol QsoJournal::schreibe". QsoJournal.h deklarierte
ContestSettings als "class" vor -- es ist ein struct.

MSVC nimmt das Schlüsselwort mit ins Namens-Mangling, clang und gcc
nicht. Deshalb baute es hier und auf Linux anstandslos, und nur der
Windows-Linker suchte ein Symbol, das so nie erzeugt wurde.

Gegengeprüft, ob dieselbe Verwechslung anderswo steht: nein.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Windows-Fehler hintereinander aus einer neuen Datei -- POSIX-only
fsync und eine class/struct-Verwechslung in der Vorwärtsdeklaration.
Beides hätte man beim Schreiben sehen können.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oe5sos
oe5sos merged commit aee5ad0 into main Sep 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant