Knopf als Flipflop

Fragen zu Tkinter.
Antworten
MrOdin
User
Beiträge: 6
Registriert: Montag 13. August 2018, 11:28

Montag 13. August 2018, 11:56

Ich würde gerne einen einfachen Knopf (tkinter.Button) als Flipflop nutzen, also das er den Wert einer Variablen zwischen zwei zuständen ändert wenn er gedrückt wird. Diese Variable ist in einer 2D-Matrix gespeichert. Ich habe 64 Knöpfe (0-63) und habe die Matrix danach angeordnet

( clm0=[0, 0, 0, 0, 0, 0, 0, 0]
clm1=[0, 0, 0, 0, 0, 0, 0, 0]
clm2=[0, 0, 0, 0, 0, 0, 0, 0]
clm3=[0, 0, 0, 0, 0, 0, 0, 0]
clm4=[0, 0, 0, 0, 0, 0, 0, 0]
clm5=[0, 0, 0, 0, 0, 0, 0, 0]
clm6=[0, 0, 0, 0, 0, 0, 0, 0]
clm7=[0, 0, 0, 0, 0, 0, 0, 0]
Display=[clm0, clm1, clm2, clm3, clm4, clm5, clm6, clm7])

Der Befehl sieht folgendermaßen aus:
def Ausgeben():
print(Display)

def change(clm, row):

print("changing")

if Display[clm][row]==1:
Display[clm][row]=0

elif Display[clm][row]==0:
Display[clm][row]=1

else:
print("Fehler")
(ich bin leider zu unfähig hier ein Bild einzufügen oder die Schleifen vernünftig einzurücken )

und wird im Knopf so aufgerufen:
bt0=tkinter.Button(main, bg="white", command=change(0, 0))
bt1=tkinter.Button(main, bg="white", command=change(0, 1))

wenn ich jetzt aber den Interpreter starte wird der Befehl "change" allerdings sofort ausgeführt (in der Shell steht "changing" ) und der Integer Display[0][0] wird zu 1. Wenn ich den Button jetzt allerdings drücke passiert nichts (kein "changing" in der shell und wenn ich mir Display ausgeben lasse ist der Wert von Display[0][0] immer noch 1). Also, was mache ich falsch?
Benutzeravatar
__blackjack__
User
Beiträge: 1040
Registriert: Samstag 2. Juni 2018, 10:21

Montag 13. August 2018, 12:14

@MrOdin: Beim erstellen des `Button` rufst *Du* `change()` auf und bindest dann den Rückgabewert dieses Aufrufs an das `command`-Argument. Also `None`. Und wenn als `command` `None` übergeben wird, dann passiert auch beim drücken des Knopfs nichts, denn `None` kann man nicht aufrufen. `command` muss ein aufrufbares Objekt sein das keine Argumente erwartet. Schau Dir mal `functools.partial()` an — damit kannst Du Dir aus `change` und den Argumenten ein solches aufrufbares Objekt erstellen.

Einen ”Flipflop”-Button kann man sich mit `tkinter.Checkbutton` erstellen. Schau Dir da mal die `indicatoron`-Option an.

Namen sollte man nicht abkürzen. Also `column` statt `clm` und `button` statt `bt`.

Wenn man anfängt Namen zu nummerieren, dann will man in der Regel bessere Namen oder eine Datenstruktur. Oft ist das eine Liste.

Namenskonvention ist klein_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `display` statt `Display` und `anzeigen` statt `Anzeigen`.

Alles was eine Funktion oder Methode ausser Konstanten verwendet, sollte als Argument herein kommen und nicht einfach auf magische Weise in der Umgebung existieren. Also weder `Anzeigen()` noch `change()` dürften einfach so `Display` verwenden.

Damit das Funktioniert musst Du Display ja auf Modulebene oder global definiert haben: Auf Modulebene gehört aber nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Das was Du beim erstellen von `Display` `clm*` nennst sind eigentlich eher Zeilen und keine Spalten‽
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
Benutzeravatar
__blackjack__
User
Beiträge: 1040
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 15. August 2018, 11:06

Mal als kleines Beispiel:

Code: Alles auswählen

from functools import partial
from pprint import pprint
import tkinter as tk

DISPLAY_SIZE = 8


def change_display(display, row, column):
    display[row][column] = not display[row][column]


def main():
    display = [[False] * DISPLAY_SIZE for _ in range(DISPLAY_SIZE)]
    
    root = tk.Tk()
    
    frame = tk.Frame(root)
    for row in range(DISPLAY_SIZE):
        for column in range(DISPLAY_SIZE):
            tk.Checkbutton(
                frame,
                indicatoron=False,
                width=2,
                bg='white',
                command=partial(change_display, display, row, column),
            ).grid(column=column, row=row)
    frame.pack()
    
    tk.Button(root, text='Print', command=partial(pprint, display)).pack()
    
    root.mainloop()


if __name__ == '__main__':
    main()
Wobei man hier zwar noch ganz gut mit `partial()` auskommt, aber für jede nicht-triviale GUI wird man um objektorientierte Programmierung (OOP) nicht herum kommen.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
MrOdin
User
Beiträge: 6
Registriert: Montag 13. August 2018, 11:28

Sonntag 16. September 2018, 16:24

Danke für die Hilfe, ich steige da zwar nicht ganz durch, aber benutze jetzt einfach Checkbuttons, was auch ganz gut funktioniert. Ums design kann ich mich bestimmt später auch noch kümmern, Hauptsache das Programm funktioniert erstmal...
Antworten