Problem in PyQt5. Wie kann man übergaben erst machen, wenn QMessage Boxen alle erfüllt sind?

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Hallo.
Ich bin gerade dabei eine GUI mit PyQt5 zu schreiben. Verzeiht mir die Frage, aber bin im Bezug auf Programmieren noch ein Anfänger.
Aufgabenstellung:
Die GUI verlangt vom Benutzer mehrere Eingaben von "float" Zahlen. Diese können mit Komma oder Punkt getrennt sein, da ich vor der Übergabe die Komma`s noch in Punkte umwandle.
Für den Fall dass der Benutzer was ganz falsches eingibt hab ich QMessageBoxen eingerichtet.
Jetzt hab ich aber noch das Problem, dass mir bei einer solchen Falsch-Eingabe die QMessageBox zwar noch angezeigt wird, aber das Programm kurz darauf trotzdem abstürzt.
Ich weiß auch warum, aber ich weiß nicht wie ich das beheben kann.
Und zwar kommt nach den ganzen Abfragen eine übergabe in eine Funktion und diese findet auch statt wenn die Eingaben falsch waren.
Hatt jemand eine Idee wie ich das abfangen kann?
Ich schreibe hier mal den ganzen bisherigen Code, aber es geht im Grunde nur um den Schluss

Wie kann ich es abfangen, dass die folgende Zeile (hab Sie ganz unten markieret)

einzelne_zufallszahl(nachkomma, oT_replaced, uT_replaced)

erst ausgelöst wird wenn die Fehler des Benutzers richtig eingegeben wurden?

Vielen Dank im Voraus für die Hilfe.

Code: Alles auswählen

import sys

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QMainWindow, QPushButton, QLabel, QRadioButton, QVBoxLayout, QHBoxLayout, QLineEdit, QGridLayout, QListView, QMessageBox
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QIcon


class MainWindow(QMainWindow):

    def __init__(self):

        super().__init__()

        # Definition des Hauptfenster's
        self.setMinimumSize(QSize(700, 500))
        self.setWindowTitle('Programm für Zufallszahlen')
        self.setWindowIcon(QIcon('Icon.png'))

        # Definition des Haupt Widget
        main_wid = QtWidgets.QWidget(self)
        self.setCentralWidget(main_wid)
        vertikales_layout = QVBoxLayout()  # vertikales Layout definieren
        main_wid.setLayout(vertikales_layout)  # das vertikale Layout als Hauptlayout definieren

        # ------------------------
        # Layout's definieren
        # ------------------------

        # Layout Zeile 1
        layout_zeile_1 = QHBoxLayout()
        layout_grid = QGridLayout()  # Kommt in Zeile 1 rein

        # Layout Zeile 2
        layout_zeile_2 = QHBoxLayout()  # Zeile 2: QLabel
        layout_zeile_3 = QHBoxLayout()  # Zeile 3: für Radio Buttons

        layout_zeile_4 = QHBoxLayout()  # Zeile 4: für Menge Zufallszahlen
        layout_zeile_5 = QHBoxLayout()  # Zeile 5: für  Berechnen und Beenden Button

        # ----------------------------------------------------------------------
        # Layouts dem Hauptlayout hinzfügen, bzw. Unterlayouts hinzufügen:
        # -----------------------------------------------------------------------

        vertikales_layout.addStretch(1)
        vertikales_layout.addLayout(layout_zeile_1)  # Zum Hauptlayout ein Horizontales Layout in Zeile 1 hinzufügen
        layout_zeile_1.addStretch(1)  # Links neben dem Grid Layout ein Stretch hinzufügen damit das GridLayout mittig dargestellt wird egal wie groß das Fenster ist
        layout_zeile_1.addLayout(layout_grid)  # Zeile 1: Grid Layout

        layout_zeile_1.addStretch(1)  # Zeile 1: Stretch rechts neben Grid einfügen

        vertikales_layout.addStretch(1)  # zwischen Zeile 1 und 2 einen Stretch einfügen

        vertikales_layout.addLayout(layout_zeile_2)  # Zeile 2: QHBoxLayout (Qlabel) hinzufügen
        vertikales_layout.addLayout(layout_zeile_3)  # Zeile 3: für RadioButtons

        vertikales_layout.addStretch(1)  # Stretch Unter RadioButtons einfügen

        vertikales_layout.addLayout(layout_zeile_4)  # Zeile 4: für Menge Zufallszahlen

        vertikales_layout.addStretch(1)  # Stretch unter Menge Zufallszahlen einfügen

        vertikales_layout.addLayout(layout_zeile_5)  # Zeile 5: für berechnen und beenden Button

        # -------------------------------------
        # Widgets definieren
        # -------------------------------------

        # Widgets für Grid definieren
        lbl1 = QLabel('Obere Toleranz der Zufallszahlen defninieren: ')
        self.lineEdit1 = QLineEdit(self)  # self.xxx ist dafür das es innerhalb der Funktion weiter benutzt werden kann

        lbl2 = QLabel('Untere Toleranz der Zufallszahlen defninieren:')
        self.lineEdit2 = QLineEdit(self)

        # Widgets für Zeile 2 definieren
        lbl4 = QLabel('Anzahl Nachkommastellen der zu erstellenden Zahlen: ')

        # Widgets für Zeile 3 defninieren
        self.radio_0 = QRadioButton('Null (0)')
        self.radio_1 = QRadioButton('Eine (1)')
        self.radio_2 = QRadioButton('Zwei (2)')
        self.radio_3 = QRadioButton('Drei (3)')
        self.radio_3.setChecked(1)  # 3 Nachkommastellen sind im Standard Aktiviert

        # Widgets für Zeile 4 definieren
        lbl5 = QLabel('Menge der gewünschten Zufallszahlen:')
        self.lineEdit5 = QLineEdit('50', self)

        # Widgets für Zeile 5 definieren
        button_berechnen = QPushButton('Berechnen')
        button_berechnen.clicked.connect(self.berechnen)

        button_beenden = QPushButton('Beenden')
        button_beenden.clicked.connect(self.beenden)

        # Widgets für Grid hinzufügen
        layout_grid.addWidget(lbl1, 1, 1)
        layout_grid.addWidget(self.lineEdit1, 1, 2)
        layout_grid.addWidget(lbl2, 2, 1)
        layout_grid.addWidget(self.lineEdit2, 2, 2)

        # Widget in Zeile 2 hinzufügen:
        layout_zeile_2.addStretch(1)
        layout_zeile_2.addWidget(lbl4)
        layout_zeile_2.addStretch(1)

        # Widget in Zeile 3 hinzufügen:
        layout_zeile_3.addStretch(1)
        layout_zeile_3.addWidget(self.radio_0)
        layout_zeile_3.addStretch(1)
        layout_zeile_3.addWidget(self.radio_1)
        layout_zeile_3.addStretch(1)
        layout_zeile_3.addWidget(self.radio_2)
        layout_zeile_3.addStretch(1)
        layout_zeile_3.addWidget(self.radio_3)
        layout_zeile_3.addStretch(1)

        # Widgets in Zeile 4 hinzufügen
        layout_zeile_4.addStretch(1)
        layout_zeile_4.addWidget(lbl5)
        layout_zeile_4.addWidget(self.lineEdit5)
        layout_zeile_4.addStretch(1)

        # Widgets in Zeile 5 hinzufügen
        layout_zeile_5.addStretch(1)
        layout_zeile_5.addWidget(button_berechnen)
        layout_zeile_5.addWidget(button_beenden)

    # Funktionen:
    def berechnen(self):
        """Mit dem berechnen Button alle Felder auswerten"""

        # Variable die es zu ausgeben / berechnen gilt:
        oT = self.lineEdit1.text()
        uT = self.lineEdit2.text()
        menge = self.lineEdit5.text()

        # Umwandlung des Feldes obere Toleranz in float
        if oT == '':
            QMessageBox.about(self, 'Hinweis zu Feld: \"Obere Toleranz\"',
                              'Das Feld für die\n\n \"Obere Toleranz\" \n\nist leer! \n\nBitte geben Sie hier das gewünschte obere Toleranzfeld an!')
        elif oT != '':
            try:
                oT_replaced = float(oT.replace(',', '.'))
                print(oT_replaced)
            except:
                QMessageBox.about(self, '!!! Fehler im Feld \"Obere Toleranz\" !!!',
                                  'Die von Ihnen gemachte Eingabe im Feld \"Obere Toleranz\" war keine Zahl. Bitte geben Sie eine richtige Zahl ein!!')
        else:
            QMessageBox.about(self, '!! ÄUẞERST SELTSAMER FEHLER !!', 'Bitte geben Sie dem Administrator \"Herr Lippert\" bescheid!')

        # Umwandlung des Feldes untere Toleranz in float
        if uT == '':
            QMessageBox.about(self, 'Hinweis zu Feld: \"Untere Toleranz\"',
                              'Das Feld für die \"Untere Toleranz\" ist leer! \n\nBitte geben Sie hier das gewünschte untere Toleranzfeld an!')
        elif uT != '':
            try:
                uT_replaced = float(uT.replace(',', '.'))
                print(uT_replaced)
            except:
                QMessageBox.about(self, '!!! Fehler im Feld \"Untere Toleranz\" !!!',
                                  'Die von Ihnen gemachte Eingabe im Feld \"Untere Toleranz\" war keine Zahl. Bitte geben Sie eine richtige Zahl ein!!')
        else:
            QMessageBox.about(self, '!! ÄUẞERST SELTSAMER FEHLER !!', 'Bitte geben Sie dem Administrator \"Herr Lippert\" bescheid!')

        # Ausgabe des Feldes "Menge der Zufallszahlen"
        if menge == '':
            QMessageBox.about(self, 'Hinweis zu \"Menge der Zufallszahlen\"',
                              'Das Feld für die Menge der Zufallszahlen ist leer!! \n\nBitte geben Sie hier die gewünschte Menge der zu erstellenden Zufallszahlen ein!')
        elif menge != '':
            try:
                menge_replaced = int(menge)
                print(menge)

            except:
                QMessageBox.about(self, '!!!Fehler im Feld \"Menge der Zufallszahlen\"!!!',
                                  'Die von Ihnen gemachte Eingabe im Feld \"Menge der Zufallszahlen\" war keine ganze Zahl. Bitte geben Sie eine ganze Zahl (ohne Kommastelle) ein!')
        else:
            QMessageBox.about(self, '!! ÄUẞERST SELTSAMER FEHLER !!', 'Bitte geben Sie dem Administrator \"Herr Lippert\" bescheid!')

        if self.radio_0.isChecked():
            nachkomma = 0
            print('Hallo ich bin 0')
            print('Die Variable nachkomma hat folgende Zahl: ', nachkomma)

        if self.radio_1.isChecked():
            nachkomma = 1
            print('Hallo ich bin 1')
            print('Die Variable nachkomma hat folgende Zahl: ', nachkomma)

        if self.radio_2.isChecked():
            nachkomma = 2
            print('Hallo ich bin 2')
            print('Die Variable nachkomma hat folgende Zahl: ', nachkomma)

        if self.radio_3.isChecked():
            nachkomma = 3
            print('Hallo ich bin 3')
            print('Die Variable nachkomma hat folgende Zahl: ', nachkomma)

       # ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        einzelne_zufallszahl(nachkomma, oT_replaced, uT_replaced)             # Folgende Zeile sollte erst ausgelöst werden wenn alles richtig ist.
       # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       
    # Ende Button
    @staticmethod
    def beenden():
        app.quit()


# -------------------------------------------------------------------------------------------------------------------------
# ------------------------ Funktionen ausserhalb Klasse -------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------------------------------

def einzelne_zufallszahl(nachkomma_aus_gui, anfangswert_aus_gui, endwert_aus_gui):
    nachkomma_funk = nachkomma_aus_gui
    anfangswert_funk = anfangswert_aus_gui
    endwert_funk = endwert_aus_gui
    print('Anzahl Nachkommastellen: ', nachkomma_funk, 'Anfangswert: ', anfangswert_funk, 'Endwert: ', endwert_funk)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    haupt_fenster = MainWindow()
    haupt_fenster.show()
    sys.exit(app.exec_())
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Attributnamen sind zumeist ziemlich schlecht. Warum heißt es lineEdit1, obwohl das doch die obere Toleranz ist. Genauso sind oT und uT schlechte Variablennamen. Wer so kryptisch schreibt, provoziert nur Fehler.
Eine Variable kann entweder == "" oder != "" sein, der else-Block ist also Quatsch. Vielmehr sollte das != "" ein else sein.
Keine nackten except, sondern immer die konkrete Exception angeben, bei Dir also ValueError.
Bei einem Fehler ist oT_replaced nicht definiert, was natürlich zu einem NameError weiter unten führt. Von daher ist Deine "Fehlerbehandlung" selbst fehlerhaft. Das Programm muß immer in einem konsistenten Zustand sein. Das einfachst wäre, bei einem Fehler die Funktion per return zu verlassen.
Statt die 4 RadioButtons einzeln abzufragen, packt man sie in eine QButtonGroup und fragt nur die ID des gedrückten Buttons ab (was gleichzeitig Dein `nachkomma`) sein kann.
In `einzelne_zufallszahl` hat das Postfix aus_gui keinen Sinn, genauso wenig das _funk, denn woher Argumente kommen, ist der Funktion egal.

Ganz schlimm ist die `beenden`-Methode, denn die benutzt die globale Variable `app`. Das Programm endet automatisch, wenn das Hauptfenster geschlossen wird.
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Hallo Sirius3,

vielen Dank für deine Antwort.
Danke für den Hinweis mit dem == und !=. Hab mir war eigentlich schon gedacht, dass mein geschriebener else Block nie zustande kommt, aber soweit hab ich nicht gedacht.
Zum Punkt:
Eine Variable kann entweder == "" oder != "" sein, der else-Block ist also Quatsch. Vielmehr sollte das != "" ein else sein.
Keine nackten except, sondern immer die konkrete Exception angeben, bei Dir also ValueError.
Den beschriebenen elif-Block hab ich jetzt raus gelöscht und ihn zum else Block umgewandelt.
Beim except war ich mir unsicher ob ich BaseException oder ValueError verwenden soll und hab es auf die schnelle zuerst weggelassen. Was ich mir nicht so anfangen sollte.

Wie ist das mit
Das einfachst wäre, bei einem Fehler die Funktion per return zu verlassen.
gemeint?
Hätte im jetztigen else-Block ein return hinzugefügt, aber das hab ich wohl falsch verstanden.

Hier ein Auszug:

Code: Alles auswählen

        # Umwandlung des Feldes obere Toleranz in float
        if oT == '':
            QMessageBox.about(self, 'Hinweis zu Feld: \"Obere Toleranz\"',
                              'Das Feld für die\n\n \"Obere Toleranz\" \n\nist leer! \n\nBitte geben Sie hier das gewünschte obere Toleranzfeld an!')
        else:
            try:
                oT_replaced = float(oT.replace(',', '.'))
                print(oT_replaced)
            except ValueError:
                QMessageBox.about(self, '!!! Fehler im Feld \"Obere Toleranz\" !!!',
                                  'Die von Ihnen gemachte Eingabe im Feld \"Obere Toleranz\" war keine Zahl. Bitte geben Sie eine richtige Zahl ein!!')
                return
                
Vielen Dank auch für den Hinweis auf die QButtonGroup. Hab schon nach sowas gesucht, aber noch Idee gehabt wie ich das machen könnte. Dank dir hab ich jetzt einen Ansatz wo ich suchen muss.
Die Funktion einezelne_zufallszahlen ist nur ein test um zu sehen, ob die Übergabe der Variablen funktionieren. Ich weiß die Namen sind sehr schlecht gewählt.

Zum Thema:
Ganz schlimm ist die `beenden`-Methode, denn die benutzt die globale Variable `app`. Das Programm endet automatisch, wenn das Hauptfenster geschlossen wird.
Das hab ich eigentlich nur so weitergeführt wie ich es aus Buch und Youtube "gelernt" habe. Wie z.b. aus einem Code aus dem Buch "Python der Grundkurs" aus dem Rheinwerk Computing Verlag von Michael Kofler
Beispieldatei qt.buttons.py (Ich hoffe ich breche hiermit keine Gesetze)

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QPushButton
from PyQt5.QtCore import QSize    

class MyWindow(QMainWindow):
    def __init__(self):
        # Konstruktor von QMainWindow aufrufen
        super().__init__()

        # Fenstergröße und Titel einstellen
        self.setMinimumSize(QSize(300, 100))    
        self.setWindowTitle('Buttons') 

        # Button-Widgets erzeugen und in Fenster einbetten
        b1 = QPushButton('Button 1', self) 
        b1.move(50, 30)
        b1.resize(120, 25)
        b1.clicked.connect(self.clicked1)
        # Größe eines Widgets ermitteln
        # print(b1.geometry().width(), b1.geometry().height())
        
        b2 = QPushButton('Button 2 (Programmende)', self) 
        b2.move(50, 60)
        b2.resize(b2.sizeHint())
        b2.clicked.connect(self.clicked2)

    def clicked1(self):
        print('Button 1 wurde angeklickt.')
    
    def clicked2(self):
        print('Button 2 wurde angeklickt.')
        print('Programmende.')
        app.quit()

# Fenster öffnen; das Programm läuft, bis das
# Fenster geschlossen wird
app = QtWidgets.QApplication([])
win = MyWindow()
win.show()
app.exec_()
Da es viel Text ist stell ich hier nochmal die Hauptfrage:
Was hab ich falsch verstanden dabei die Funktion per return zu verlassen?

Vielen Dank für die Ausführliche Analyse meines ('Anfänger'-) Codes und die Hilfestellung.
Ich wäre sehr dankbar wenn du mir nochmal helfen könntest.

Viele Grüße
Medds
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Das mit dem `return` ist doch richtig.
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Sorry.Weiß nicht was ich vorher falsch gemacht habe. Das mit return im except-Block funktioniert glaub ich doch.
Vielen Dank nochmal
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Im Internet steht viel falsches und Bücher sind oft nicht viel besser.
Am besten verfährt man mit der offiziellen Dokumentation.

Code: Alles auswählen

button_beenden.clicked.connect(self.close)
Und die letzten Zeilen gehören in eine Funktion, damit man erst gar nicht in Versuchung kommt, globale Variablen zu benutzen (und exec statt exec_).

Code: Alles auswählen

def main():
    app = QtWidgets.QApplication(sys.argv)
    hauptfenster = MainWindow()
    hauptfenster.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Mit der offizellen Dokumentation hab ich oft so meine Probleme, weil sie nicht in meiner Muttersprache geschrieben ist.
Seit ich angefangen hab mit Programmieren zu lernen werd ich zwar im Englisch immer besser, aber für die Dokumentation reicht es dann oft leider immer noch nicht ganz.
Und mit Übersetzungs-Programmen wird es oft auch nicht viel besser.
Mit knapp 40 lernt man sich leider nicht mehr so leicht.

Zum Thema exec:
Dachte in Python gibt es einen Konflikt mit exec und deshalb wurde es in exec_ umbenannt. (oder so in der Art)
Das hab ich sogar auf eurem forum gelesen.
PepeCyB hat geschrieben:
Der Tip aus dem qtforum.de hat mir die Lösung gebracht.

Nach Studium der PyQT-Referenz bin ich darauf gestoßen, daß die
Member-Funktion exec() in QDialog aus besagtem Grund (Konflikt
mit der Python-internen exec-Anweisung) in exec_loop() umbenannt
wurde.
Tja... da hatte ich doch nicht alle Informationsquellen ausgeschöpft :K
und durchgelesen... ich gelobe Besserung.
von dem eintrag auf:
viewtopic.php?t=9562

Danke nochmal für den Hinweis auf die QButtonGroup.
Versuche das gerade umzusetzen. Mal schauen ob ich mir das heute noch zusammenreimen kann.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast da einen 14 Jahre alten Thread ausgegraben.
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Sorry das ich nochmal Frage:
Versuche gerade die QButtonGroup umzusetzen.
Kann man auf die schnelle sagen was ich da falsch mache?

Code: Alles auswählen

        radio_0 = QRadioButton('Null (0)', self)
        radio_1 = QRadioButton('Eine (1)', self)
        radio_2 = QRadioButton('Zwei (2)', self)
        radio_3 = QRadioButton('Drei (3)', self)
        button_group = QButtonGroup(self)
        layout_zeile_3.addWidget(button_group)
        button_group.addButton(radio_0)
        button_group.addButton(radio_1)
        button_group.addButton(radio_2)
        button_group.addButton(radio_3)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Medds: ``exec`` war in Python 2 ein Schlüsselwort, deshalb muss damals die Methode anders benannt werden, darum hiess sie `exec_()`. In Python 3 ist ``exec`` kein Schlüsselwort mehr und PyQt5 hat deshalb `exec()` und `exec_()` als Namen für die gleiche Methode. Damit Code von Python 2 leichter auf Python 3 portiert werden konnte. In PyQt6 wird es nur noch `exec()` geben und `exec_()` nicht mehr. Macht also Sinn sich das jetzt schon abzugewöhnen, dann braucht man das später beim Wechsel von PyQt5 auf PyQt6 nicht mehr ändern.

Nummerierte Namen sind in der Regel falsch. Da will man entweder bessere Namen verwenden, oder gar keine Einzelnamen, sondern eine Datenstruktur. Oft eine Liste.

Ansonsten: `QButtonGroup` ist kein Wigdet. Was der `TypeError` den Du da bekommst, auch sagt:

Code: Alles auswählen

TypeError: addWidget(self, QWidget, stretch: int = 0, alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 'QButtonGroup'
Das Objekt sorgt dafür das die `QRadioButton`-Objekte semantisch zusammen gehören, in die GUI einsetzen musst Du aber die `QRadioButton`-Objekte selbst zum Layout hinzufügen. Man kann den Radiobuttons in der Gruppe ganzahlige IDs zuordnen was prima zu den Nachkommastellen passt:

Code: Alles auswählen

        ...
        self.precision_button_group = QButtonGroup(self)
        for precision, text in enumerate(["Null", "Eine", "Zwei", "Drei"]):
            button = QRadioButton(
                f"{text} ({precision})", checked=precision == 0
            )
            layout_zeile_3.addWidget(button)
            self.precision_button_group.addButton(button, precision)
        ...
Und in der Weiterverarbeitung dann:

Code: Alles auswählen

    def berechnen(self):
        ...
        precision = self.precision_button_group.checkedId()
        print(f"Anzahl Nachkommastellen: {precision}.")
An dem Beispiel aus dem Kofler sind auch die abgekürzten und nummerierten Namen sehr schlecht gewählt, und die Platzierung und Grösse mit absoluten Pixelwerten sind auch ein No-Go, das man IMHO gar nicht erst zeigen sollte.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Vielen Dank für die ausführliche Erkärung.

Mit dieser Erklärung könnte es sogar ich schaffen.
Antworten