Beiträge von __blackjack__

    llutz Ich weiss nicht so recht, Zeichenkettenoperationen auf Dateipfaden — das ist klassischerweise eigentlich ein Fall für ``basename``:

    Bash
    for bild in ordner/*.jpeg; do gm composite overlay.png "$bild" output/"$(basename "$bild")"; done

    Chris1893 Falls es sich um viele Bilder handelt und es sich lohnt das auf mehrere Prozessoren/Kerne zu verteilen, ist GNU parallel ein einfacher Weg:

    Bash
    parallel gm composite overlay.png {} output/{/} ::: ordner/*.jpeg

    Und mit `PIL` ginge es so (ungetestet):

    Okay, folgendes ist mit Vorsicht zu geniessen, da ich weder wirklich Ahnung von dem Tiny13 habe für den das BASCOM-Programm ist, noch von BASCOM selbst. Hier wäre meine Interpretation von dem Programm in Python mit `gpiozero`:

    MagicBird So funktioniert ``while`` nicht. Wenn der Körper der Schleife ausgeführt wird, dann gibt es mindestens einen Eintrag, denn die Schleife wird ja nur durchlaufen wenn `fetch()` einen Eintrag liefert und wird nicht durchlaufen falls die Methode keinen Eintrag liefert. Also kann man auch nicht *in* der Schleife testen wollen ob es einen Eintrag gibt/gab.

    Da die ID hier ja wohl eindeutig ist, macht eine Schleife auch nicht wirklich Sinn, denn es gibt ja dann nur die Fälle es gibt einen Benutzer mit der ID oder es gibt ihn nicht. Du willst da also eher ein ``if`` haben.

    Ungetestet:

    MagicBird Wenn Du fragen zu Code hast der nicht funktioniert, solltest Du den Code zeigen der nicht funktioniert, statt irgend etwas das so ähnlich aussieht, den Fehler dann aber vielleicht gar nicht mehr hat, der im Original ist. Du kannst natürlich den Code vereinfachen und unnötiges raus werfen, aber dann immer testen ob in dem vereinfachten Code auch immer noch das Problem tatsächlich vorhanden ist!

    Am besten ist ein lauffähiges, minimales Beispiel, das hier jeder nachstellen kann. Beispielsweise in dem Du eine Funktion schreibst, die eine kleine Testdatenbank mit SQLite erstellst, und die dann die Funktion die den Fehler hat, aufruft. Das kann dann jeder nachvollziehen ohne sich eine MySQL-Datenbank aufsetzen zu müssen.

    Wie kommst Du jetzt plötzlich auf CSS‽

    MagicBird Du formatierst da Text in eine SQL-Abfrage — das ist eine ganz schlechte Idee. Wenn Du Glück hast ist das nur ineffizient oder führt zu Fehlern, wenn Du Pech hast, ist das eine fette Sicherheitslücke. Du willst hier nicht `query()` verwenden, sondern `prepare()` und `execute()`.

    `fetch()` liefert keinen Wahrheitswert sondern rein Array. Der Vergleich mit ``true`` macht also nicht so wirklich Sinn.

    Und falls nur interessant ist *ob* etwas gefunden wird, aber nicht *was*, würde man das nicht mit ``SELECT * …`` machen, sondern mit einer EXISTS-Abfrage.

    Es ist auch ein bisschen komisch das die Tabelle anscheinend dynamisch ist, dafür aber alle Tabellen eine `text`-Spalte haben. Das könnte auf einen schlechten/falschen Datenbankentwurf hindeuten.

    Wobei die Erklärung nicht ganz stimmt, denn wenn man der Shell irgend was mit * drin gibt und eine Fehlermeldung zu einer nicht gefundenen Datei kommt in der der * noch enthalten ist (!), dann gab es schlicht keine Dateien auf die das Muster passte und die Shell hat da nichts expandiert. Das passiert im Beispiel unweigerlich wenn es im Ausgabe-Ordner noch nichts, oder zumindest noch keine JPEG-Dateien gibt, denn dann wird ``/output/*.jpeg`` exakt *so* übergeben, was dann zu der Fehlermeldung führt, weil `composite` alle Dateinamen so wie es dort steht als *Eingabedateien* verarbeiten will. `composite` könnte damit schon was anfangen, macht aber nicht das was gewünscht ist.

    @Brownbear: Grrr, jetzt habe ich das Video angesehen — was für eine verschwendete Zeit. Du gingst davon aus ich hätte es nicht gesehen — warum? Denn ich weiss nicht mehr als vorher und dementsprechend ändert sich da auch nichts an meiner Einstellung oder Argumentation gegen das generieren von Quelltext wenn man diesen Schritt auch leicht umgehen kann.

    UI-Dateien sind XML was mit Python nichts zu tun hat, wird irgendwie so gesagt als wenn das schlecht wäre. UI-Dateien haben halt auch nicht viel mit Programmcode zu tun, darum ist das überhaupt nicht schlecht, dass das kein Programmquelltext ist. Grössere Layouts will man auch gar nicht als Quelltext lesen, weil das massiv unübersichtlich ist, und ja auch der Grund warum man überhaupt den Designer verwenden will.

    Wobei die Aussage in dem Video, dass man das, was man da an generiertem Code sieht, alles zu Fuss schreiben müsste, natürlich auch nicht stimmt. Da ist viel das eigentlich unnötig ist, und in Code hat man im Gegensatz zum Designer auch die Möglichkeit *Code* zu schreiben und zu nutzen, also Funktionen/Methoden und Schleifen, damit man sich wiederholende Sachen nicht tatsächlich als Code schreiben muss. Ich habe das Beispiel aus dem Video mal im Designer nachgestellt (ordentlich mit Layouts statt mit absolten Positions- und Grössenangaben) und das ist der mit pyuic5 generierte Quelltext:

    Den Wust will doch kein Mensch wirklich lesen. Erst recht nicht wenn die GUI nicht mehr so klein und einfach aufgebaut ist.

    Von Hand geschrieben sähe das so aus *und* hat auch gleich die Reaktion auf die Auswahl eines Radiobutton mit etwas sprechender Ausgabe als einfach nur nummeriert von 1 bis 6:

    Und das ganze noch mal mit laden der .ui-Datei:

    Zurück zum Video: Importe so abzukürzen ist furchtbar und auch bei anderen Namen wird viel zu viel abgekürzt. (`wnd` für `window`, Modulname `mqt`, `mgui` für?) Aussagekräftige Namen sind ”Rotz” den man nicht immer schreiben will, und `rb1` bis `rb6` sind vertretbare Namen‽ ? Ja nee, nicht wirklich.

    Alles auf Modulebene und aus der generierten Klasse, die eigentlich als Mixin gedacht ist, wird ein Exemplar erstellt. Argh. Er sagt ja selbst er versteht nicht so ganz was da passiert. Dann sollte man vielleicht auch keine Erklärvideos dazu machen wo man eine falsche Verwendung zeigt. ?

    Das „eine Datei“-Argument ist keins. Das interessiert keinen ausser Leute, die versuchen wollen ihren Code zu verstecken. Eine Datei für den Endnutzer bekommt man auch wenn man einfach alles in eine Archivdatei steckt. Wobei ich mir auch fast sicher bin, dass man auch .ui-Dateien in Qt-Ressourcen stecken kann. Und man kann bei den üblichen Werkzeugen um ”eine” Datei aus Python-Programmen zu erzeugen auch Datendateien mit verpacken. Letztlich bekommt halt ein ZIP-Archiv mit einem kleinen EXE-Kopf, der jedes mal wenn man das Programm ausführt, alles in ein temporäres Verzeichnis entpackt, also den Python-Interpreter, die Standardbibliothek, Qt, die Python-Anbindung an Qt, andere Module von Drittanbietern, die Module des Programms selbst. Das wird alles entpackt und dann ausgeführt. Ich finde das an sich schon irre, aber unter Linux noch bekloppter, weil die ganzen Sachen ausser dem eigenen Code dort ja schon da sind, oder zumindest über die Linux-Paketverwaltung installiert werden können.

    Man hat mit den .ui-Dateien einen Arbeitsschritt weniger. Und man kann auch alles unter Versionsverwaltung stellen, ohne das man nach dem auschecken, entweder selbst, oder wenn das jemand anderes tut, noch irgendwelche Dateien generieren muss, um das Programm dann komplett zu haben. Denn generierte Dateien gehören nicht in die Versionsverwaltung.

    Generierten Code will man in der Regel auch gar nicht sehen, denn Codegeneratoren sind in der Regel nicht darauf ausgelegt was zu erzeugen was Menschen lesen sollen. Man hat bei Python ein bisschen Glück, dass die Sprache gewisse Sachen erzwingt, die das lesen einfacher machen, aber das was da aus den .ui-Dateien erzeugt wird ist kein Code an dem man sich für eigene Programme orientieren sollte. Man kann sich das ja mal generieren um rein zu schauen, aber das ist kein Grund für die Benutzung davon.

    bug-reporter Ich muss unter Python *sehr* selten auf diese Art debuggen. Ich wüsste bei vielem Code nicht mal wo ich mit einem Einzelschrittdebugger ansetzen sollte, weil die “Schritte“ in der Regel in Funktionen, „comprehensions“, und Generatorausdrücken stecken. Funktionen sind dann selten überhaupt komplex genug als dass man da nicht verstehen würde was passiert. Wenn man auf testbaren Code achtet, kann man einzelne Funktionen auch leicht interaktiv ausprobieren. Und einige Fehler findet man auch beim schreiben von Unit-Tests.

    Bei der Fehlersuche nutze ich Protokollausgaben, die entweder sowieso schon gemacht werden, oder die ich als Debug-Level dann einbaue. In einfachen Fällen auch mal simple `print()`-Anweisungen an strategisch interessanten Stellen. Oder so etwas wie das `icecream`-Modul, was einem da ein bisschen mehr Informationen mit ”Magie” ausgibt.

    SteffenMurks Das die Mail in irgendeinem Postausgang gespeichert wird ist die Aufgabe des Programms das die Mail versendet. SMTP ist ein Transportprotokoll (das T steht für „transport“), das hat überhaupt nichts mit irgendwelchen Postfächern zu tun. Der SMTP-Server dem Du das schickst, wird das in der Regel selbst wieder an einen anderen SMTP-Server schicken. Da können mehrere auf dem Weg sein, die das weitergeben, bis es beim Dienstanbieter ist, den der Empfänger verwendet, und wo er sich dass dann über ein anderes Protokoll abholen kann.

    Wenn Dein Mailprogramm die versendeten Mails in einem Postausgang auf dem Server Deines Mail-Anbieters speichert, dann kopiert das die Mail in der Regel per IMAP-Protokoll dort hin, nachdem es die Mail versendet hat.

    Es stimmt halt nicht so ganz mit dem SMTP-Protokoll überein: Zeilen werden dort mit "\r\n" beendet und nicht nur mit "\n". Die meisten Mailserver kommen zwar auch mit "\n" klar, aber anscheinend nicht alle.

    Und im Beispielcode von hyle (nicht in dem verlinkten Quelltext) fehlt die Leerzeile zwischen den Headerdaten und dem E-Mail-Inhalt.

    Hier jetzt kein Problem weil es in keinem Beispiel vorkam, aber man muss auch noch aufpassen, dass man keine Zeile im E-Mail-Inhalt hat, in der ein einzelner Punkt steht, weil dass das Ende des Inhalts kennzeichnet. Sieht man auch in der `send()`-Methode, die genau das macht.

    Und SMTP ist eigentlich ASCII-only. Wenn man da mehr verschicken möchte, muss man das entsprechend der Protokolle/Spezifikationen kodieren, oder hoffen, dass die MTAs, Mailserver, und Mailprogramme das trotzdem akzeptieren und irgendwie sinnvoll darstellen.

    PhiGer2525: Zu dem schöner/besser: Statt "Hauptprogramm" als Kommentar hin zu schreiben würde man das besser in eine `main()`-Funktion stecken. Dann ist auch ohne Kommentar klar, dass es sich um die Hauptfunktion handelt, und es gibt keine globalen Variablen mehr.

    Magische Zahlen im Quelltext sollte man vermeiden. Die Pin-Nummer würde man als Konstante am Anfang definieren. Dann weiss man an jeder Stelle wo die benutzt wird, was die Zahl bedeutet, und man kann den Pin auch leichter und vor allem sicherer ändern, weil man das nur a einer Stelle im Quelltext machen muss, und nicht Gefahr läuft vorkommen von 18 zu übersehen, oder eine 18 zu ersetzen die eine andere Bedeutung hatte.

    `a` ist ein schlechter Name und auch überflüssig, weil der Vergleich wo das benutzt wird *immer* das Ergebnis `True` haben wird. Also kann man da auch einfach `True` hin schreiben.

    Namen sollten keine kryptischen Abkürzungen enthalten. Besonders verwirrend ist hier das es die Funktion `wr_log()` gibt, deren Ergebnis an einen Namen `write_log` gebunden wird, der eigentlich der passende Name für die Funktion wäre. Bei `make_call` (das Ergebnis!) und `call()` ist es ähnlich. Funktionen/Methoden werden nach Tätigkeiten benannt, um sie leichter von eher passiven Werten unterscheiden zu können. Was nicht mehr funktioniert wenn man die eher passiven Werte auch nach Tätigkeiten benennt.

    Man nummeriert auch keine Namen. `call1()` und `call2()` verraten dem Leser nichts darüber was diese Anrufe *bedeuten* — genau dass ist doch aber wesentlich wichtiger zu wissen, als welches der erste und welches der zweite Anruf ist. *Die* Information wird bereits aus der Aufrufreihenfolge der beiden Funktionen deutlich.

    Mit vielen Rückgabewerten wird in dem Code auch überhaupt nichts gemacht — dann sollte man die auch nicht an Namen binden. Damit erledigt sich dann auch gleich ein Teil der nummerierten Namen.

    Zwischenstand (ungetestet):

    Die API von der `wr_log()`-Funktion ist nicht so schön. Man muss da immer ": " mit übergeben um das Datum von der Nachricht getrennt zu protokollieren. Und das der Zeitstempel nicht wie in jeder anderen Protokolldatei am *Anfang* steht, halte ich auch für ungünstig. Überhaupt würde ich da nichts selber programmieren was es nicht schon gibt, entweder in der Standardbibliothek im `logging`-Modul, oder in externen Bibliotheken wie `loguru` in bunt und in Farbe.

    ibex44: Der Code zum Vergleich auf den Temperaturbereich funktioniert, aber `ti` und `talt` enthalten immer die selben Werte, also ``ti[i] = talt[i]`` ist sinnlos weil das letztendlich keinen Effekt hat ausser ein ganz kleines bisschen Prozessorzeit zu verbrauchen.

    Bei einfachen Zuweisungen (und Parameterübergaben bei Aufrufen) kopiert Python niemals von sich aus Werte. Die erste Zeile in der Funktion ``ti = talt`` bedeuten das die *selbe* Liste ab dort unter beiden Namen erreichbar ist. Was letztlich auch bedeutet, dass der Rückgabewert sinnfrei ist, weil das immer die gleiche Liste ist, die auch übergeben wurde.

    Du solltest dringend an der Namensgebung arbeiten. Namen sollte keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Funktionsnamen beschreiben die Tätigkeit die von der Funkion durchgeführt wird.

    Man muss auch nicht jedes kleine Zwischenergebnis an irgendeinen Namen binden.

    Wenn Du was mit Indexzugriffen machst, ist das in den allermeisten Fällen „unpythonisch“. Zum Beispiel weil Du nicht über die Werte sondern über einen Index iterierst, über den dann auf Werte zugegriffen wird, oder weil Du Listen veränderst, statt einfach neue zu erstellen. Hier sollte einfach eine neue Liste aufgebaut werden. Das ist einfacher und weniger fehleranfällig, und auch leichter zu verstehen, weil man dann nicht erst schauen muss welche Werte `i` annehmen kann, und ob auch tatsächlich jeder Index in jeder Liste behandelt wird oder ob es Ausnahmen gibt.

    Apropos Ausnahmen: `Exception` ist nur sehr selten tatsächlich die Ausnahme die sinnvoll behandelt werden kann. Man behandelt nur Ausnahmen bei denen der Inhalt vom ``except``-Block eine *sinnvolle* Behandlung darstellt. `Exception` greift auch wenn man sich im ``try``-Block beispielsweise bei einem Namen vertippt hat.

    Statt ``not a == b`` würde man besser ``a != b`` schreiben.

    ``float(format(ti[i], ".2f"))`` ist ziemlich umständlich für ``round(ti[i], 2)`` — allerdings würde man an dieser Stelle im Code nicht runden. Runden nur wenn man tatsächlich mit dem gerundeten Wert weiterrechnen will. Und `round()` ist nicht für die Ausgabe von Werten — dafür verwendet man die diversen Werkzeuge zur Formatierung. Also zum Beispiel das was Du zum runden missbraucht hast.

    ``if (ti[i] > 60) or (ti[i] < -30):`` ist umständlich und weniger verständlich ausgedrückt als ``if not -30 <= ti[i] <= 60:``. ``ti[i]`` muss da dann auch nur einmal ausgewertet werden.

    Zwischenstand:

    Wenn ein Sensor nicht erkannt wird, werden gleiche alle Sensoren nicht abgefragt — das ist IMHO ein ziemlich heftiger Datenverlust. Ebenfalls verloren geht die Information welche Messung(en) nicht funktioniert haben, oder aus dem Bereich fallen. Letztere würde ich ja einfach 1:1 aufzeichnen und bei nicht erfolgreichen Messungen `math.nan` liefern. Diese Werte bei der Auswertung dann durch den vorherigen Messwert ersetzen kann man dann immer noch bei der Auswertung machen, aber man hätte die Informationen halt noch. Beispielsweise für Auswertungen speziell auf Fehler. Ob die sich zeitlich auffällig häufen oder so, das kann man ja alles gar nicht mehr auswerten wenn man diese Informationen weg wirft.

    Ist denn überhaupt garantiert, dass `get_available_sensors()` die Sensoren immer in der gleichen Reihenfolge liefert? Wo werden Sensoren einsortiert die dazu kommen, wo falle welche Weg?

    SteffenMurks Auf dem „Vintage Computing Festival Berlin“ gab's 2019 einen Vortrag von René Meyer „Computer in der DDR“. Vielleicht ist da ja der Rechner dabei den ihr verwendet habt.

    Ich verstehe das ein Anfänger eventuell etwas erschlagen ist, aber dagegen kann ich nicht wirklich etwas machen, ausser gar nicht zu antworten. Denn ich weiss nicht was „Anfänger“ konkret bedeutet, also wie viel Wissen und wie viel Bereitschaft sich etwas zu erarbeiten vorhanden ist, und auch nicht wie schwer oder leicht das fällt. Das ist ja alles total individuell.

    So ein ausschalten wenn das Display längere Zeit inaktiv ist, könnte man in der Tat mit einem Thread lösen. Und man muss dann bei gemeinsam genutzten Ressourcen auch darauf achten, dass da immer nur ein Thread zur gleichen Zeit drauf zugreifen kann. Das `threading`-Modul bietet dafür beispielsweise `Lock` und `RLock` an. Und es reicht tatsächlich nicht nur im Display-Code darauf zu achten, man muss auch in anderem Code der den Bus verwendet, sicherstellen, dass da immer nur ein Thread zur gleichen Zeit drauf zugreift.

    Praktisch könnte man sowohl den Bus als auch ein Lock von aussen in den Display-Code rein reichen und beides auch an anderen Code übergeben, der den Bus verwendet.

    Alternativ könnte man das gesamte Programm so gestalten, dass es eine Hauptschleife gibt, die eine Warteschlange mit Ereignissen abarbeitet, und Zugriffe aus anderen Threads nicht aus diesem Threads direkt passieren, sondern nur in der Hauptschleife, also im Hauptthread.

    Insgesamt ist nebenläufige Programmierung, egal ob mit Threads, Prozessen, ``async``/``await``, … eine ziemlich komplexe und fehleranfällige Angelegenheit.

    SteffenMurks Ich habe Dich überhaupt nicht kritisiert. Ich habe den Code kritisiert — ohne zu schauen von wem der letztendlich kommt. Konstruktiv, in dem ich Verbesserungen vorgeschlagen habe. Konstruktive Kritik als persönlichen Angriff und Attacke auffassen kann man machen, bringt einen aber nicht wirklich weiter. Und sich angegriffen fühlen wegen Kritik an Code den man gar nicht selber geschrieben hat, löst bei mir jetzt ein bisschen Verwunderung aus.

    Das ich nicht unfehlbar bin weiss ich und gebe ich auch gerne zu. Und finde es gut wenn man mich auf die Fehler hinweist, weil ich dann die Chance habe daraus zu lernen und es besser zu machen. Ich habe das Problem ja auch verstanden und adressiert denke ich — man darf bestimmte Aktionen wie das Anschalten des Displays nur machen wenn es ausgeschaltet ist, weil im eingeschalteten Zustand nicht einfach nichts passiert, sondern der Inhalt gelöscht wird.

    Die „Supernacht“ war eine Nacht in der ich Leerlauf hatte und nach längerer Abwesenheit hier mal wieder ins Forum schaute. Da bin ich einfach die letzten drei Seiten der aktuellen Themen durchgegangen ob's da Quelltext gibt, den man mal überarbeiten könnte. Die Häufung kommt also im ”laufenden Betrieb” eher nicht vor. Meine ”Rückkehr” hat übrigens ein Forumsmitglied zu verantworten, das in einem anderen Forum fragte ob, nicht mal wieder hier reinschauen mag. (Wäre aber von selbst demnächst wiedergekommen, weil der Weihnachtskram im Supermarkt den nächsten Advent of Code ins Gedächtnis gerufen hat. ?)

    Ich habe Rückmeldungen bekommen, die mich doch ein bisschen überrascht haben, und als ich überlegte, ob ich hier vielleicht doch nicht mehr willkommen bin, gab es aber auch positive Reaktionen auf die Beiträge von Mitgliedern die schon vor der Pause aktiv waren. Nicht nur über die Funktion unten rechts beim jeweiligen Beitrag, sondern auch per privater Konversation.

    Dennis89 Müsste es nicht „Kack-Kodes“ heissen? Heugabel (und Fackel) gibt's anscheinend nicht als Emoji. Tut's auch ein ?? ?

    „Zeiger“ ist halt falsch, das wäre auf Englisch „pointer“ und bezeichnet wie schon gesagt etwas anderes. „Flag“ wird normalerweise nicht auf deutsch übersetzt. Ich habe dafür schon „Merker“ gesehen/gelesen, aber das ist wirklich eher unüblich. Ich persönlich versuche auch zu viel Englisch zu vermeiden wo es deutsche Begriffe gibt. Guess what, genau das wurde mir auch schon vorgeworfen, warum ich mich denn so komisch unverständlich Deutsch ausdrücke. Man kann es offenbar nicht jedem Recht machen. Hast Du eine Quelle für die Verwendung von „Zeiger“ als „flag“ in Ostdeutschland? Was wurde denn dort dann zu „pointer“ gesagt? Eine Doppelbelegung würde ich nahezu ausschliessen, weil das zu *sehr* viel Verwirrung führen würde.

    Wenn Dir Begrifflichkeiten egal sind, dann wird das mit der Kommunikation schwierig. Und auch wenn Du nur Antworten haben willst, in denen nichts steht was Du nicht nachschlagen musst. Jemand der antwortet, weiss ja auch gar nicht was Du nachschlagen musst, und was nicht. Wenn alle immer nur Antworten schreiben, bei denen sie davon ausgehen, dass nichts dem Leser unbekanntes dabei ist, müsste man sich entweder sehr einschränken, oder in jedem Beitrag ganze Abhandlungen mit allen Hintergründen schreiben. In einem Programmier(unter)forum gehe ich davon aus, dass die Grundbegriffe entweder bekannt sind, oder leicht nachgelesen werden können. Zum Beispiel in der Dokumentation der Programmiersprache oder bei Wikipedia. Sollte es dabei Probleme oder Verständnisschwierigkeiten geben, kann ja nachgefragt werden. Ist ja ein Forum.

    Wie sollte das mit der Rücksprache denn aussehen? In einem öffentlichen Diskussionsforum erst einmal Leute über Privatnachrichten befragen ob und was man denn in dem Thema schreiben darf, wäre nicht nur sehr unüblich, die meisten Leute die ich kenne würden sich auch solche Privatnachrichten verbitten. Wenn Dir ein Code-Review nicht passt, kannst Du es ja auch einfach ignorieren.

    Was kompletten Quelltext gegenüber nur Änderungen angeht: Da habe ich zwei Zeilen geändert, die man so wie ich es gezeigt habe leicht identifizieren kann. Hätte ich nicht nur den kurzen betroffenen Abschnitt gezeigt, sondern das ganze fast unveränderte Programm, hätte ich mit Beschwerden gerechnet, das nicht klar ist was geändert wurde, und ob man dafür wirklich noch mal den gesamten Quelltext hätte posten müssen. Aber das kann ich natürlich auch machen:

    Und noch mal die Veränderung als Diff:

    (Interessant: Diff steht nicht bei der Auswahl der Syntax-Highlights im Beitragseditor zur Verfügung, wird aber offenbar automagisch erkannt.)