Menubutton add_checkbutton ausgewählt darstellen

Fragen zu Tkinter.
Antworten
Andrey35
User
Beiträge: 16
Registriert: Donnerstag 8. Oktober 2020, 23:00

Hallo zusammen,

Habe in einer GUI , um etwas Platz zu sparen, mehrere Checkbuttons in ein Menü eingefügt.
Nun würde ich gerne in einem anderem Fenster auch ein Menü mit Checkbuttons erzeugen, wo manche Checkbuttons "gecheckt" sind.

Code: Alles auswählen

        for check_bearbeiten in range(7,Spaltenanzahl[0]):
            cursor.execute("Select " + str(Spaltennamen[check_bearbeiten][0]) +" From Benutzer where Matrikelnummer =%s", (Matrikelnummer_zum_Bearbeiten,))
            Daten = cursor.fetchone()
            
            if Daten[0] == 0:
                int_Sp_Bearbeiten = IntVar()
                mb.menu.add_checkbutton(label=Spaltennamen[check_bearbeiten][0], variable=int_Sp_Bearbeiten)
                ListeCheckbox_Bearbeiten.append(int_Sp_Bearbeiten)
            else:
                int_Sp_Bearbeiten = IntVar()
                mb.menu.add_checkbutton(label=Spaltennamen[check_bearbeiten][0], variable=int_Sp_Bearbeiten)

                ListeCheckbox_Bearbeiten.append(int_Sp_Bearbeiten)
Hab mal im Internet nach einer Lösung gesucht und bisher nichts gefunden :(
Das Hauptproblem liegt darin ,dass bei "mb.menu.add_checkbutton" ein NoneType generiert wird und man dann nicht mit select() checken kann.
Benutzeravatar
__blackjack__
User
Beiträge: 14031
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Andrey35: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Auch Inhaltlich sind viele nicht gut. Wenn man keinen Einzelwert hat, sondern eine Sequenz, dann sollte man das am Namen ablesen können. `Spaltenanzahl` wäre *eine* Spaltenanzahl. Das scheint aber eine Liste oder ähnliches zu sein.

`check_bearbeiten` ist ein komischer Name für einen Index. Das klingt auch sehr nach einer Tätigkeit, also als wenn das eine Funktion oder Methode und kein eher passiver Wert ist.

Eigentlich willst Du dauch gar keinen Index sondern über Elemente von `Spaltennamen` iterieren. Was da komisch aussieht, ist das da anscheinend auch wieder Sequenzen drin sind, von denen Du immer das erste Element (Index 0) verwendest. Was ist denn da noch drin? Oder ist das ein Ergebnis einer Datenbankabfrage und Du hast die einelementigen einfach nur nicht ”ausgepackt”? Falls das der Fall ist, ist das bei `Spaltenanzahl` auch so? Das wäre echt furchtbar verwirrend wenn Du da überall einelementige Tupel weiterverwendest und deswegen dauernd ``[0]`` überall dranstehen muss. Das löst man *einmal* und nicht jedes mal wenn man auf so einen Wert zugreifen will.

Der Ausdruck ``Spaltennamen[check_bearbeiten][0]`` steht drei mal in dem Quelltextabschnitt, statt das das *einmal* an einen Namen gebunden wird.

`Matrikelnummer_zum_Bearbeiten`? Gibt es noch andere in diesem Kontext/Namensraum? Kann das nicht einfach `makrikelnummer` heissen?

Man kann den Abgefragten Wert direkt als Wahrheitswert verwenden falls da kein NULL in den Spalten stehen kann (und es nicht okay ist den als `False` zu interpretieren).

`ListeCheckbox_Bearbeiten` klingt wieder nach eine Tätigkeit, nicht nach passiven Werten. Grunddatentypen haben in Namen nichts verloren.

Die beiden Zweige von dem ``if``/``else`` enthalten den gleichen Code, das gehört da also nicht zweimal hin. Ich nehme an in der Leerzeile im ``else`` wolltest Du ein `select()` unterbringen. Das wäre dann aber das einzige was diese beiden Zweige unterscheidet und auch das einzige was da drin stehen müsste. Womit ein ``if`` ausgereicht hätte. Da man `tkinter.Variable`-Objekten auch gleich beim Erstellen einen Wert mitgeben kann, braucht man aber überhaupt keine Fallunterscheidung sondern setzt einfach den abfragten Wert ein. Und vielleicht auch in ein `tkinter.BooleanVar`.

`mb`? Abkürzungen sind doof. Namen sollen dem Leser die Bedeutung von den Werten dahinter vermitteln, nicht zum Rätselraten zwingen.

Zeichenketten und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC denn Python. In Python gibt es dafür Zeichenkettenformatierung mit der `format()`-Methode und f-Zeichenkettenliterale.

Die magische 7 und `Spaltenanzahl` und warum diese `range()` Sinn macht, sollte dringend erklärt werden. Das sieht sehr komisch aus.

Zwischenstand:

Code: Alles auswählen

        spalten_check_variables = []
        #
        # TODO Magische 7 und den Endwert erklären warum/welchen Sinn das macht,
        #   und warum das immer korrekt ist.  Für die 7, falls das nicht besser
        #   lösbar ist, eine sinnvoll benannte Konstante definieren.
        #
        for was_auch_immer_das_hier_ist in spaltennamen[
            7 : spaltenanzahlen[0]
        ]:
            spaltenname = was_auch_immer_das_hier_ist[0]
            cursor.execute(
                f"SELECT {spaltenname} FROM benutzer WHERE matrikelnummer=%s",
                [matrikelnummer],
            )
            variable = tk.BooleanVar(value=cursor.fetchone()[0])
            mb.menu.add_checkbutton(label=spaltenname, variable=variable)
            spalten_check_variables.append(variable)
Für jeden Wert eine neue Abfrage zu starten, wo die doch alle zusammen in einem Datensatz stehen, ist komisch. Da würde man eher alle auf einmal abfragen:

Code: Alles auswählen

        #
        # TODO Magische 7 und den Endwert erklären warum/welchen Sinn das macht,
        #   und warum das immer korrekt ist.  Für die 7, falls das nicht besser
        #   lösbar ist, eine sinnvoll benannte Konstante definieren.
        #
        ausgewaehlte_spaltennamen = [
            spaltenname[0]
            for spaltenname in spaltennamen[7 : spaltenanzahlen[0]]
        ]
        cursor.execute(
            "SELECT {} FROM benutzer WHERE matrikelnummer=%s".format(
                ",".join(ausgewaehlte_spaltennamen)
            ),
            [matrikelnummer],
        )
        spalten_check_variables = []
        for spaltenname, wert in zip(
            ausgewaehlte_spaltennamen, cursor.fetchone()
        ):
            variable = tk.BooleanVar(value=wert)
            mb.menu.add_checkbutton(label=spaltenname, variable=variable)
            spalten_check_variables.append(variable)
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Andrey35
User
Beiträge: 16
Registriert: Donnerstag 8. Oktober 2020, 23:00

__blackjack__ hat geschrieben: Samstag 16. Januar 2021, 02:23
`check_bearbeiten` ist ein komischer Name für einen Index. Das klingt auch sehr nach einer Tätigkeit, also als wenn das eine Funktion oder Methode und kein eher passiver Wert ist.

Eigentlich willst Du dauch gar keinen Index sondern über Elemente von `Spaltennamen` iterieren. Was da komisch aussieht, ist das da anscheinend auch wieder Sequenzen drin sind, von denen Du immer das erste Element (Index 0) verwendest. Was ist denn da noch drin? Oder ist das ein Ergebnis einer Datenbankabfrage und Du hast die einelementigen einfach nur nicht ”ausgepackt”? Falls das der Fall ist, ist das bei `Spaltenanzahl` auch so? Das wäre echt furchtbar verwirrend wenn Du da überall einelementige Tupel weiterverwendest und deswegen dauernd ``[0]`` überall dranstehen muss. Das löst man *einmal* und nicht jedes mal wenn man auf so einen Wert zugreifen will.

Der Ausdruck ``Spaltennamen[check_bearbeiten][0]`` steht drei mal in dem Quelltextabschnitt, statt das das *einmal* an einen Namen gebunden wird.

`Matrikelnummer_zum_Bearbeiten`? Gibt es noch andere in diesem Kontext/Namensraum? Kann das nicht einfach `makrikelnummer` heissen?

Man kann den Abgefragten Wert direkt als Wahrheitswert verwenden falls da kein NULL in den Spalten stehen kann (und es nicht okay ist den als `False` zu interpretieren).

`ListeCheckbox_Bearbeiten` klingt wieder nach eine Tätigkeit, nicht nach passiven Werten. Grunddatentypen haben in Namen nichts verloren.

Die beiden Zweige von dem ``if``/``else`` enthalten den gleichen Code, das gehört da also nicht zweimal hin. Ich nehme an in der Leerzeile im ``else`` wolltest Du ein `select()` unterbringen. Das wäre dann aber das einzige was diese beiden Zweige unterscheidet und auch das einzige was da drin stehen müsste. Womit ein ``if`` ausgereicht hätte. Da man `tkinter.Variable`-Objekten auch gleich beim Erstellen einen Wert mitgeben kann, braucht man aber überhaupt keine Fallunterscheidung sondern setzt einfach den abfragten Wert ein. Und vielleicht auch in ein `tkinter.BooleanVar`.

`mb`? Abkürzungen sind doof. Namen sollen dem Leser die Bedeutung von den Werten dahinter vermitteln, nicht zum Rätselraten zwingen.

Zeichenketten und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC denn Python. In Python gibt es dafür Zeichenkettenformatierung mit der `format()`-Methode und f-Zeichenkettenliterale.

Die magische 7 und `Spaltenanzahl` und warum diese `range()` Sinn macht, sollte dringend erklärt werden. Das sieht sehr komisch aus.

Hey Blackjack,

Danke dir für das ausführliche Feedback. Werde ab jetzt versuchen meine Codes etwas sauberer und korrekter zu schreiben.

Die magische 7 in der Range steht dafür, dass ich die Daten, die ich aus der Datenbank rausziehe in eine Liste packe und die, welche ab der 7-Stelle stehen benötige für diesen Fall. (Ich weiß etwas unschön, aber bin auch noch nicht so lange am programmieren :/ )

Matrikelnummer ist schon als variable vergeben. Das Bearbeiten steht immer dafür, dass ich im "Hauptfenster" einen Benutzer erstelle und diesen in die Datenbank einfüge. Dann will ich ein Fenster, wo ich die Daten von eingefügten Benutzern bearbeiten kann.

__blackjack__ hat geschrieben: Samstag 16. Januar 2021, 02:23

Die beiden Zweige von dem ``if``/``else`` enthalten den gleichen Code, das gehört da also nicht zweimal hin. Ich nehme an in der Leerzeile im ``else`` wolltest Du ein `select()` unterbringen. Das wäre dann aber das einzige was diese beiden Zweige unterscheidet und auch das einzige was da drin stehen müsste. Womit ein ``if`` ausgereicht hätte. Da man `tkinter.Variable`-Objekten auch gleich beim Erstellen einen Wert mitgeben kann, braucht man aber überhaupt keine Fallunterscheidung sondern setzt einfach den abfragten Wert ein. Und vielleicht auch in ein `tkinter.BooleanVar`.


Zwischenstand:

Code: Alles auswählen

        spalten_check_variables = []
        #
        # TODO Magische 7 und den Endwert erklären warum/welchen Sinn das macht,
        #   und warum das immer korrekt ist.  Für die 7, falls das nicht besser
        #   lösbar ist, eine sinnvoll benannte Konstante definieren.
        #
        for was_auch_immer_das_hier_ist in spaltennamen[
            7 : spaltenanzahlen[0]
        ]:
            spaltenname = was_auch_immer_das_hier_ist[0]
            cursor.execute(
                f"SELECT {spaltenname} FROM benutzer WHERE matrikelnummer=%s",
                [matrikelnummer],
            )
            variable = tk.BooleanVar(value=cursor.fetchone()[0])
            mb.menu.add_checkbutton(label=spaltenname, variable=variable)
            spalten_check_variables.append(variable)
Für jeden Wert eine neue Abfrage zu starten, wo die doch alle zusammen in einem Datensatz stehen, ist komisch. Da würde man eher alle auf einmal abfragen:

Code: Alles auswählen

        #
        # TODO Magische 7 und den Endwert erklären warum/welchen Sinn das macht,
        #   und warum das immer korrekt ist.  Für die 7, falls das nicht besser
        #   lösbar ist, eine sinnvoll benannte Konstante definieren.
        #
        ausgewaehlte_spaltennamen = [
            spaltenname[0]
            for spaltenname in spaltennamen[7 : spaltenanzahlen[0]]
        ]
        cursor.execute(
            "SELECT {} FROM benutzer WHERE matrikelnummer=%s".format(
                ",".join(ausgewaehlte_spaltennamen)
            ),
            [matrikelnummer],
        )
        spalten_check_variables = []
        for spaltenname, wert in zip(
            ausgewaehlte_spaltennamen, cursor.fetchone()
        ):
            variable = tk.BooleanVar(value=wert)
            mb.menu.add_checkbutton(label=spaltenname, variable=variable)
            spalten_check_variables.append(variable)
Danke dir für die Zeilen. Sieht auf jedenfall viel sauberer aus, als meine Lösung. Aber das löst noch nicht mein Problem, soweit ich es verstehe ?
Benutzeravatar
__blackjack__
User
Beiträge: 14031
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Andrey35: Wenn ich dein Problem verstanden habe, dann löst es das. `tkinter.Variable`-Objekte funktionieren in beide Richtungen. Da kann man nicht nur Abfragen was im verbundenen Widget angezeigt wird, sondern beim ändern (oder setzen) des Wertes wirkt sich das auch auf den Zustand des Widgets aus. Ansonsten müsstest Du noch mal erklären was das Problem ist, denn dann hätte ich das nicht verstanden.

Was heisst `Matrikelnummer` ist schon als Variable vergeben? In *dem* Namensraum? Wofür?
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Andrey35
User
Beiträge: 16
Registriert: Donnerstag 8. Oktober 2020, 23:00

__blackjack__ hat geschrieben: Samstag 16. Januar 2021, 17:26 @Andrey35: Wenn ich dein Problem verstanden habe, dann löst es das. `tkinter.Variable`-Objekte funktionieren in beide Richtungen. Da kann man nicht nur Abfragen was im verbundenen Widget angezeigt wird, sondern beim ändern (oder setzen) des Wertes wirkt sich das auch auf den Zustand des Widgets aus. Ansonsten müsstest Du noch mal erklären was das Problem ist, denn dann hätte ich das nicht verstanden.

Was heisst `Matrikelnummer` ist schon als Variable vergeben? In *dem* Namensraum? Wofür?
Hey Blackjack,

tut mir leid für die Umstände.
Hat zu Beginn nicht geklappt, aber hab das nochmal ausprobiert und jetzt funktioniert es, wie ich es will! Vielen Dank dir!

Mit der GUI will ich verschiedene Benutzer in eine Datenbank anlegen und für das anlegen habe ich den Namen "Matrikelnummer" schon benutzt und würde es für mich selbst ungern nochmal verwenden.
Bei dem Part, wo du mir geholfen hast werden vorhandene Benutzer bearbeitet. Deswegen habe in diesem Fall "Matrikelnummer_bearbeiten" gemacht. :|

Wünsche dir ein schönes Wochenende noch :D
Benutzeravatar
__blackjack__
User
Beiträge: 14031
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Andrey35: Das ist Wahnsinn überall immer nur Namen verwenden zu wollen, die man woanders nicht schon mal verwendet hat. Das skaliert doch so gar nicht. Insbesondere wenn das doch nun mal für eine Matrikelnummer steht, ist `matrikelnummer` der passende Name dafür. Der steht ja in einem Kontext, das heisst den kann man immer zusammen mit der Funktion/Methode sehen in der er verwendet wird. Wenn die beispielsweise `bearbeite_benutzer()` oder so heisst, dann ist klar das die `matrikelnummer` die des zu bearbeitenden Benutzers ist. Ein Aspekt von Funktionen/Methoden ist ja gerade das es eine Schnittstelle nach aussen gibt und alles was nur *in* der Funktion/Methode lokal benannt wird, nicht mit irgendetwas ausserhalb interagiert und man auch bei einem mehrere zig-tausend Zeilen langen Programm sich keinen Kopf darum machen muss, dass man lokal Namen verwenden muss die es nirgendwo anders im Programm schon mal als lokale Namen gibt.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Antworten