buttons in forschleife generieren

Fragen zu Tkinter.
Antworten
silvapuer
User
Beiträge: 7
Registriert: Dienstag 11. Februar 2020, 16:41

hallo,
ich möchte eine Tastatur automatisch generieren lassen.
dazu sollen Buttons erzeugt und anhand eines Dictionaries konfiguriert werden.
das ganze soll durch eine forSchleife passieren.

hier mein code:

Code: Alles auswählen

from tkinter import Tk, Button
root = Tk()
BUTTONDICT = ({"0":{"command":print("0"), "text":"0"},
                        "1":{"command":print("1"), "text":"1"},
                        "2":{"command":print("2"), "text":"2"},
                        "3":{"command":print("3"), "text":"3"},
                        "4":{"command":print("4"), "text":"4"},
                        "5":{"command":print("5"), "text":"5"},
                        "6":{"command":print("6"), "text":"6"},
                        "7":{"command":print("7"), "text":"7"},
                        "8":{"command":print("8"), "text":"8"},
                        "9":{"command":print("9"), "text":"9"},
                        "komma":{"command":print(","), "text":","},
                        "gleich":{"command":print("="), "text":"="},
                        "plus":{"command":print("+"), "text":"+"},
                        "minus":{"command":print("-"), "text":"-"},
                        "mal":{"command":print("mal"), "text":"\u22C5"},
                        "durch":{"command":print("durch"), "text":":"},
                        "hoch":{"command":print("hoch"), "text":"^"},
                        "wurzel":{"command":print("sqrt"), "text":"\u221A"},
                        "sinus":{"command":print("sin"), "text":"sin( )"},
                        "cosinus":{"command":print("cos"), "text":"cos( )"},
                        "tangens":{"command":print("tan"), "text":"tan( )"},
                        "logarithmus":{"command":print("log"), "text":"log( )"},
                        "EXP":{"command":print("EXP"), "text":"\u22C5"+"10^( )"},
                        "EXE":{"command":print("EXE"), "text":"EXE"} })

buttonlist = ["7","4","1","komma","8","5","2","0","9","6","3","EXP","mal","durch","plus","gleich","hoch","wurzel","minus","EXE","logaritmus","sinus", "cosinus","tangens"]

co=ro=0
for button in buttonlist:
            b = Button(root, text=str(BUTTONDICT[button["text"]]), command=BUTTONDICT[button["command"]])
            b.grid(collumn=co, row=ro)
            
            ro += 1
            if ro >= 4:
                ro=0
                co += 1
                
root.mainloop()
das mit deem dictionarie und der Liste ist so, weil es in zukunft mehrere Listen geben wird, und jenachdem, weche genutzt wird soll die tastatur aussehen. das dictionarie ist dabei eine übersicht aller möglichen Buttons.

Mein Problem: es erschein ein Error "TypeError: string indices must be integers". ich vermute, dass das irgendwo ein problem mit der Variable "button" gibt, die ja durch die Liste geht, alsoein element der liste nach dem anderen symbolisiert.

habt ihr eine idee, wie ich das anders schreiben kann oder den Fehler beheben?

danke schonmal im vorraus!
danke für eure hilfe :)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Schau Dir doch mal an, was ›button‹ ist, dann weißt Du auch warum ›button["text"]‹ nicht geht.
silvapuer
User
Beiträge: 7
Registriert: Dienstag 11. Februar 2020, 16:41

Sirius3 hat geschrieben: Dienstag 25. Februar 2020, 22:21 Schau Dir doch mal an, was ›button‹ ist, dann weißt Du auch warum ›button["text"]‹ nicht geht.
Na button ist eine Variable, die Nacheinander die Strings der buttonlist symbolisiert.
Und da der Index eines Dictionaries durch einen String angegeben wird, müsste ds doch eigentlich funktionieren...
danke für eure hilfe :)
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Dann musst du die Variable auch als String kennzeichnen mit " ".
Zuletzt geändert von Jankie am Donnerstag 27. Februar 2020, 16:43, insgesamt 2-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Das sind alles Strings in dem Ausdruck, das ist ja gerade das Problem, es wird versucht das hier auszuführen:

Code: Alles auswählen

In [1]: "7"["command"]                                                          
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-ce458ab4b17c> in <module>
----> 1 "7"["command"]

TypeError: string indices must be integers
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Ahh stimmt. Allerdings scheint mir der Zugriff auf ein Dict auch falsch zu sein.

Das geht doch mit dict[x][y] und nicht mit dict[x[y]], oder?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Das kommt ganz darauf an wie `dict` aufgebaut ist und was `x` und `y` für Werte sind. Wenn `x` ein Wörterbuch und `y` ein passender Schlüssel oder `x` eine Liste und `y` eine ganze Zahl ist, dann kann auch ``dict[x[y]]`` gehen wenn bei ``x[y]`` ein Wert heraus kommt der als Schlüssel in `dict` verwendbar ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
silvapuer
User
Beiträge: 7
Registriert: Dienstag 11. Februar 2020, 16:41

Also ich habs jetzt raus bekommen.

Falls die Nachwelt nochmal eine ähnliche Frage hat, hir ist der funktionierende Code:

Code: Alles auswählen

from tkinter import Tk, Button
root = Tk()

def func_0():
        pass
def func_1():
        pass
def func_2():
        pass
def func_3():
        pass
def func_4():
        pass
def func_5():
        pass
def func_6():
        pass
def func_7():
        pass
def func_8():
        pass
def func_9():
        pass
def func_gleich():
        pass
def func_plus():
        pass
def func_minus():
        pass
def func_mal():
        pass
def func_durch():
        pass
def func_hoch():
        pass
def func_wurzel():
        pass
def func_sinus():
        pass
def func_cosinus():
        pass
def func_tangens():
        pass
def func_logarithmus():
        pass
def func_EXP():
        pass
def func_EXE():
        pass
def func_komma():
        pass
        
BUTTONDICT = {"0":{"command":func_0, "text":"0"},
             "1":{"command":func_1, "text":"1"},
             "2":{"command":func_2, "text":"2"},
             "3":{"command":func_3, "text":"3"},
             "4":{"command":func_4, "text":"4"},
             "5":{"command":func_5, "text":"5"},
             "6":{"command":func_6, "text":"6"},
             "7":{"command":func_7, "text":"7"},
             "8":{"command":func_8, "text":"8"},
             "9":{"command":func_9, "text":"9"},
             "komma":{"command":func_komma, "text":","},
             "gleich":{"command":func_gleich, "text":"="},
             "plus":{"command":func_plus, "text":"+"},
             "minus":{"command":func_minus, "text":"-"},
             "mal":{"command":func_mal, "text":"\u22C5"},
             "durch":{"command":func_durch, "text":":"},
             "hoch":{"command":func_hoch, "text":"^"},
             "wurzel":{"command":func_wurzel, "text":"\u221A"},
             "sinus":{"command":func_sinus, "text":"sin( )"},
             "cosinus":{"command":func_cosinus, "text":"cos( )"},
             "tangens":{"command":func_tangens, "text":"tan( )"},
             "logarithmus":{"command":func_logarithmus, "text":"log( )"},
             "EXP":{"command":func_EXP, "text":"\u22C5"+"10^( )"},
             "EXE":{"command":func_EXE, "text":"EXE"} }

buttonlist = ["7","4","1","komma","8","5","2","0","9","6","3","EXP","mal","durch","plus","gleich","hoch","wurzel","minus","EXE","logarithmus","sinus", "cosinus","tangens"]

co=ro=0
for button in buttonlist:
    
    b_info = BUTTONDICT.get(button)
    b = Button(root, text=b_info.get("text"), command=b_info.get("command"))
    b.grid(column=co, row=ro)        
    ro += 1
    if ro >= 4:
        ro=0
        co += 1
                
root.mainloop()
:idea:
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@silvapuer: Was nicht wirklich richtig ist. `get()` verwendet man wenn man sich nicht sicher ist ob der Schlüssel im Wörterbuch vorkommt oder nicht, weil man dann `None` als Ergebnis bekommt statt eines `KeyError`. Dieser Fall darf bei Deinem Code aber niemals auftreten, denn wenn es auf erster Ebene passiert, dann führt das unweigerlich zu einem `AttributeError` weil `None` keine `get()`-Methode hat. Wenn es erst auf zweiter Ebene passiert, dann wird `command` an den Wert `None` gebunden. Der `Button` tut dann einfach nichts wenn man darauf klickt.

Ich frage mich sowieso warum `BUTTONDICT` als Wörterbuch angelegt ist wenn man die Informationen auch gleich in eine Liste stecken könnte und sich die zusätzliche Liste und sich den dadurch folgenden Mehraufwand beim Zugriff sparen kann.

Grunddatentypen haben nichts in Namen zu suchen.

Die ganzen `fun_*()`-Funktionen sind zu viel. Da wird es Gruppen von Funktionen geben die nahezu das gleiche machen werden, wo man also jeweils nur *eine* Funktion braucht, mit entsprechenden Argumenten.

`co` und `ro` sind schlechte Namen. Wenn das `column` und `row` heissen soll, dann sollte man die auch so nennen.

Deren Berechnung ist mit einem zusätzlichen Zähler in der Schleife (`enumerate()`) und `divmod()` einfacher als das hochzählen und anpassen und hochzählen zu bewerkstelligen.

Da das alles auf Modulebene steht ist GUI aber IMHO noch nichts was Du machen solltest. Für GUIs braucht man objektorientierte Programmierung. Also lern am besten erst einmal wie man sinnvolle Klassen schreibt, dann musst Du nicht *das* *und* die ereignisbasierte Programmierung lernen die mit GUI-Programmierung dazu kommt.

Wörterbücher die immer den gleichen festen Satz von Schlüsseln haben sind übrigens auch besser Objekte von einem entsprechenden Datentyp. Im einfachsten Fall ein Typ der mit `collections.namedtuple()` erstellt wurde. Aber hier macht es vielleicht auch gleich Sinn den Code für die jeweilige Operation mit in das Objekt zu stecken und dann später auf ein Objekt anzuwenden das den Rechnerzustand modelliert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@silvapuer: `root` sollte nicht definiert werden, und dann kommen erst lange Funktionsdefinitionen (die man wahrscheinlich fast alle zu einer zusammen fassen könnte). Sondern `root` und alles ab `co=ro=0` gehört auch in eine Funktion, die man normalerweise `main` nennt, und die ganz am Ende aufgerufen wird.
Wenn man column und row meint, sollte man das nicht abkürzen, zumal Du bei ro exakt einen Buchstaben sparst, huhu!
Column und row lassen sich auch ganz leicht per divmod aus einem laufenden Index berechnen.
Wenn Du keinen Defaultwert willst (also sinnvoll damit weiterarbeiten kannst), solltest Du nicht `get` bei Dictionaries benutzen, sondern die eckigen Klammern.
Datentypen sollten nicht in Variablennamen vorkommen, Konstanten schreibt man komplett GROSS.

Code: Alles auswählen

from tkinter import Tk, Button

# Funktionsdefinitionen func_0 ...
        
BUTTON_INFOS = {
    "0":{"command":func_0, "text":"0"},
    "1":{"command":func_1, "text":"1"},
    "2":{"command":func_2, "text":"2"},
    "3":{"command":func_3, "text":"3"},
    "4":{"command":func_4, "text":"4"},
    "5":{"command":func_5, "text":"5"},
    "6":{"command":func_6, "text":"6"},
    "7":{"command":func_7, "text":"7"},
    "8":{"command":func_8, "text":"8"},
    "9":{"command":func_9, "text":"9"},
    "komma":{"command":func_komma, "text":","},
    "gleich":{"command":func_gleich, "text":"="},
    "plus":{"command":func_plus, "text":"+"},
    "minus":{"command":func_minus, "text":"-"},
    "mal":{"command":func_mal, "text":"\u22C5"},
    "durch":{"command":func_durch, "text":":"},
    "hoch":{"command":func_hoch, "text":"^"},
    "wurzel":{"command":func_wurzel, "text":"\u221A"},
    "sinus":{"command":func_sinus, "text":"sin( )"},
    "cosinus":{"command":func_cosinus, "text":"cos( )"},
    "tangens":{"command":func_tangens, "text":"tan( )"},
    "logarithmus":{"command":func_logarithmus, "text":"log( )"},
    "EXP":{"command":func_EXP, "text":"\u22C5"+"10^( )"},
    "EXE":{"command":func_EXE, "text":"EXE"}
}

BUTTONS = [
    "7","4","1","komma",
    "8","5","2","0",
    "9","6","3","EXP",
    "mal","durch","plus","gleich",
    "hoch","wurzel","minus","EXE",
    "logarithmus","sinus", "cosinus","tangens"
]

def main():
    root = Tk()
    for index, button in enumerate(BUTTONS):
        button_info = BUTTON_INFOS[button]
        button = Button(root, **button_info)
        column, row = divmod(index, 4)
        button.grid(column=column, row=row)
    root.mainloop()

if __name__ == '__main__':
    main()
Antworten