"andere" buttons inerhalb eines GRID ansprechen/verändern

Fragen zu Tkinter.
Antworten
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

Hallo Allerseits,

bemühe mich TKinter kennen und verstehen zu lernen....
wo ich derzeit hänge: wie kann man bitte einen Zahl/String in den Typ "tkinter.xx" umwandeln?

ausführlicher erklärt...versuche ich Folgendes zu erreichen:
innerhalb eines Fensters habe ich mehrere Buttons mittels GRID erstellt..
...und kann auch beim Betätigen jedes einzelnen Buttons eine Änderung/Befehlsabfolge machen.

Wo ich jetzt leider hänge, ist:
beim Drücken "eines bestimmten" Haupt-Buttons....soll die Befehlsabfolge und Buttondarstellung von "mehreren anderen Buttons" laufen
(konkret/vereinfacht: 2 Buttons für 2 unterschiedliche Lampen...und ein 3. Button, um Alle Lampen zu schalten.... wenn man den 3. Button drückt, sollen eben auch die Buttons 1+2 Ihren Status ändern, etc..)
Habe bzw wollte dies nun so lösen, dass ich bei Betätigung des 3.Buttons die TKinter-Button-Codes (vorher gespeichert und abfragbar in einem Dictionary..bei mir zB .1982119536,..) der Buttons 1+2 aufrufe...und dann damit weitermache....allerdings sind die Button Codes 1+2 aus dem Dictionary dann Zahlen/Strings...und TKinter benötigt für den Aufruf des jeweiligen Buttons ja den Typ: <class 'tkinter.xx'> (bei mir derzeit <class 'tkinter.Button'>).

Frage: wie kann man also bitte einen Zahl/String in den Typ "tkinter.xx" umwandeln? (oder wie kann ich eventuell anders die "anderen Buttons 1+2" ansprechen/ablaufen lassen?)

Ich hoffe, dies war veständlich.

Besten Dank im Vorhinein für Eure Hilfe.
msmn
Benutzeravatar
__blackjack__
User
Beiträge: 14092
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@msmn: Wenn Du die `Button`-Objekte brauchst, dann speicher halt die `Button`-Objekte im Wörterbuch und nicht deren Tk-Namen/Pfade. Es macht keinen Sinn sich die Tk-Namen/Pfade zu merken, die letztlich nur ein internes Detail sind, um dann später zu versuchen aus Tk irgendwie wieder Python-Objekte heraus zu bekommen, wenn man doch die Python-Objekte bereits hat(te) und sich nur merken braucht.
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@_blackjack_: Danke
habe versucht Deinen TIpp aufzunehmen.
jedoch bin ich leider nicht gut genug im Python/TKinter...um das ohne weitere Erklärung umzusetzen.

wenn ich es richtig verstehe, sind die Button-Objekte ja zB: text, width, height, activebackground,..
diese hätte ich ja grundsätzlich....
was ich aber nicht weiss, ist wie/wo TKinter (GRID) zB die Positionen der Buttons abspeichert, etc...
...und das würde man ja wahrscheinlich zur Änderung der Buttons alles benötigen, oder?

Hättest Du bitte eventuell ein kurzes Beispiel....(oder eventuell den Link zu einem Beispiel) wo mit Button-Objekt-Daten "gearbeitet" wird?

Sorry...und nochmals Danke!!
msmsn
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hast du nicht richtig verstanden. Was du beschreibst sind bestenfalls die Attribute eines Button-Objekts.

Code: Alles auswählen

ein_button = Button(....)
weist ein Button-Objekt einem Namen zu. Und auch nachdem das dargestellt und gedrückt wird etc kannst du zB mit

Code: Alles auswählen

ein_button[“text”] = “egal”
darauf noch nachträglich Einfluss nehmen.

Wissen wo dieser Button steckt ist dazu auch nicht nötig.
Benutzeravatar
__blackjack__
User
Beiträge: 14092
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@msmn: `Button`-Objekte *sind* nicht text, width, height, … die *haben* diese Eigenschaften. Das Objekt fast das ja alles zusammen. Objekte fassen Zustand/Eigenschaften und Methoden die darauf operieren zu einem Wert zusammen. Und so einen Wert kann man an Namen oder Attribute binden, in Listen stecken oder andere Datenstrukturen stecken – was man eben so mit Werten machen kann. Objektorientierte Programmierung ist etwas was man möglichst schon *vor* GUI-Programmierung drauf haben sollte, denn man braucht das für jede nicht-triviale GUI. Also je nach dem was da konkret gemacht werden soll müsstest Du die `Button`-Objekte als einzelne Attribute auf Deiner GUI-Klasse speichern oder zum Beispiel in einer Liste auf dem Objekt.

Und das alles hat nichts mit `grid()` zu tun oder wie auch immer die angeordnet werden.
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@: _deets_ und _Blackjack_:
vorerst "Danke" an Euch beide!
möchte jetzt erst Eure Antworten verarbeiten/ausprobieren, bevor ich eventuell wieder lästig werde und nachfrage...;-)

...und: ich habe den Hinweis auf OOP verstanden...damit muss ich mich wirklich noch intensiver auseinandersetzen!

vielleicht trotzdem noch gleich folgende Frage, auf welcher ich dann aufbauen kann:
wenn ich zB: "ein_button = Button(....)" mache, wie kann ich dann herausfinden, "welche Attribute es ALLE gibt" für ein-button (also zB: text, width, ...) …(oder auch gleich für Button(...))?

msmn
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

Ergänzung:
der Befehl "print (ein_button)" gibt eben nur aus :z.B " .!button2" (ohne irgendwelche anderen Eigenschaften)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Eigenschaften sind alle ein der Dokumentation beschrieben. Und das print gibt dir halt nur ein relativ uninteressanten bezeichner zurück. Keine Darstellung von allen Eigenschaften. Hätte man machen können. Ist aber so leider nicht.
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

ich tue mir scheinbar noch schwer mit dem "Nicht-Verwechseln" von Objekten, Eigenschaften Attributen, Methods, etc...mir kommt vor, in jedem Manual sind diese etwas anders bezeichnet?! (liegt aber sicher noch an meinem Unverständnis)

schreib mir doch bitte einen konkreten Print-Befehl, um eine Eigenschaft des "ein_button" (oder "Button()") auszugeben.....dann kämpfe ich mich weiter mit den Manuals durch...;-)

danke
msmn
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Besser wäre mal ein Python Tutorial durch zu arbeiten. Denn das ist ja nun ein allgemeines Thema. Nicht spezifisch zu tkinter. Und wozu willst du die Eigenschaft ausgeben? Ich glaube nicht, das dir das etwas bringt. Aber bitte:

print(ein_button[“text”])
Benutzeravatar
__blackjack__
User
Beiträge: 14092
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@msmn: Bei Tk kommt das Problem dazu, das manchmal Tk/Tcl-Begriffe dazu kommen die eben in Tk/Tcl üblich sind, in Python aber nicht. Das was man in Tk/Tcl beim erstellen der Objekte angeben und mit `config()`/`configure()` oder per […]-Zugriff abfragen und ändern kann, nennt sich dort Optionen. Wegen der Schreibweise wie man das in Tcl angibt, die sich der Entwickler der Sprache bei Kommandozeilenprogrammen abgeschaut hat. Beispiel wie man dort ein Label erzeugt und anordnet:

Code: Alles auswählen

label .info -text "Some informational text." -fg red -bg black
pack .info -side top
Optionen sind keine Attribute! Attribute sind die Werte eines Objekts auf die man in Python per Punktoperator zugreifen kann. Bei ``person.age`` wäre `age` ein Attribut auf dem Objekt das an den Namen `person` gebunden ist.

Auslesen von einzelnen Optionen geht von Python aus auch per […]-Zugriff, wie __deets__ das gezeigt hat. Das ist aber eher ungewöhnlich und wird nur sehr selten gemacht, denn Tcl ist „stringly typed“, dass heisst die Programmiersprache kennt im Grunde nur Zeichenketten als Datentyp, und dementsprechend liefert so eine Abfrage von Pythonseite immer Zeichenketten – auch für numerische Werte.

Die Optionen sind bei den `Widget`-Objekten im Docstring aufgeführt, da kommt man also in einer Python-Shell auch über die `help()`-Funktion heran (oder bei IPython auch über ein ``?`` am Ende).

Ansonsten gibt es beispielsweise die Dokumentation von Effbot, die hier schon verlinkt wurde, und man kann natürlich immer in die Dokumentation vom Original schauen: http://www.tcl.tk/man/tcl8.5/TkCmd/button.htm
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@_deets_: Danke!
Musterbefehl 'print(ein_button[“text”]' half mir trotzdem …(hatte natürlich meinerseits vorher herumprobiert....mit Klammer, Punkt, unterschiedlichen Hochkommas, etc...aber nicht mit eckigen Klammern)
wie auch immer...jetzt kann ich weiter versuchen dies besser zu verstehen

@_blackjack_: ebenfalls Danke!
Deine Erklärung für "Attribut" ist sehr verständlich/hilfreich....
insbesondere auch Danke für die Links!

jetzt geb ich einmal Ruhe und versuch das alles zu verinnerlichen
msmn
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

bin (leider) wieder da....;-)

@deets:
Du hast ja weiter oben beschrieben, dass es sinnvoll wäre "... ein Button-Objekt einem Namen …" zuzuordnen, also zB:
ein_button = Button(....)

wenn ich jetzt "mehrere/viele" Buttons habe, wie würde dann bitte die Zuweisung lauten, sodass die Variablennamen "automatisch hochgezählt werden können" (also zB statt "ein_button" dann "button001, button002,..."...oder ginge das doch irgendwie mit Dictionaries).?
(Anmerkung1: bei meiner letztendlichen Anwendung hätte ich "viele" Buttons....etwa 150....welche mittels GRID dann platziert und betätigt werden können)
(Anmerkung2: habe es mit Listen, Tupels, Dictionaries probiert....scheint aber nicht zu funktionieren aufgrund der unterschiedlichen Typen gegenüber dem TKinter)
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

@msmn: das geht mit Listen, und wenn Du weiterhin keinen Code postest, dann hört diese Raterei hier nie auf, weil wir nicht wissen können, was Du denn da probiert hast und was dann nicht funktioniert hat.
Benutzeravatar
__blackjack__
User
Beiträge: 14092
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@msmn: Das klingt nach Listen mit Listen mit `Button`-Objekten, wobei die verschachtelten Listen mit der Anordnung im `grid()` korrespondieren sollten, wenn man da per Zeilen- und Spaltennummer auf den jeweiligen `Button` zugreifen können soll.
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

stelle hier jetzt mal meinen (vereinfachten) Code hier herein...
(habe dabei versucht vieles von den Befehlsabfolgen bei den jeweils gedrückten Buttons, etc..rauszuschmeissen, sodas dieser übersichtlicher/lesbarer wird).
Um was es geht: ich möchte mit den Buttons zB unterschiedlichste Lichter/Lämpchen auf meiner Modellbahnanlage mittels Raspberry Pi und GPIOs sowie MCP23017 ansteuern....
...und natürlich sollte der eine oder andere Button dann auch auf Wunsch "mehrere" Lämpchen auf einmal schalten (also zB die Gebäude-Lichter in einem ganzen Strassenzug)

was ich dazu gerne hätte, ist dass zB bei Betätigung des Buttons "links oben"....einige/alle anderen Buttons "ihren Zustand optisch wechseln" (und dann natürlich auch hinterlegte Befehlsfolgen ablaufen)

ich hoffe, dies hilft zum besseren Verständnis meines Problems....sorry, dass ich Euch so bemühe.

Code: Alles auswählen

from tkinter import * 
from functools import partial
import time
import sys

########Definition der Variablen
#listenelemente_max_spalten=3 
listenelemente_max_zeilen=7
position_dir={'Start':0}  

matrixname= 'Lichtermatrix'
fenster_max_spalten=5         # Spalten(in jeder Reihe)
fenster_max_reihen=listenelemente_max_zeilen//fenster_max_spalten
if (listenelemente_max_zeilen%fenster_max_spalten)>0:
    fenster_max_reihen=fenster_max_reihen + 1
fenster_width=12
fenster_height=3

#####################################
########Definition des Listenarrays
hauptliste = [["0=Position","1=Bezeichnung",      "2=Adresse","3=Ein/Aus","4=MCS23017-Hex-Adresse","5=MCS-Bank","6=MCS-Adresse"]]
hauptliste =hauptliste + [[ 1,"Alle Lichter","ALLE","E/A(0,1)",0,"leer",0]]
#                        Pos0, Pos1     ,      Pos2,  Pos3, Pos4, Pos5, P6
hauptliste =hauptliste + [[ 2,"Licht  2",        29,     0,    0,  "A", 0]]
hauptliste =hauptliste + [[ 3,"Licht  3",        31,     1,    0,  "A", 0]]
hauptliste =hauptliste + [[ 4,"Licht  4",        33,     0,    0,  "A", 0]]
for ii in range (4,listenelemente_max_zeilen):
    hauptliste =hauptliste +[[ii+1,"Licht, Lampe."+str(ii+1).zfill(3),37,0,0,"",0]]

def bei_mausklick(geklickt): 
    if hauptliste[geklickt][3]==1:
        print ("mach was EIN")
    elif hauptliste[geklickt][3]==0:
        print ("mach was AUS")
            
def toggle1(button):
    geklickt=position_dir[str(button)]
    if geklickt == 1:
        button.flash()                      #damit blinkt der Button kurz
        print ("270 hier sollte dann ALLE Lichter geschalten werden...und alle Buttons entsprechend zB die Farbe ändern")
    elif geklickt <= listenelemente_max_zeilen:
        button.state = not button.state
        zw_text = hauptliste[geklickt][1]+" "+str(hauptliste[geklickt][2])+" "+str(hauptliste[geklickt][4])+" "+hauptliste[geklickt][5]+" "+str(hauptliste[geklickt][6])
        if button.state == 1:
            button['text'] = zw_text + " AUS"
            button.config (bg='brown')  
            hauptliste[geklickt][3]=0
        if button.state == 0: 
            button['text'] = zw_text + " EIN"
            button.config (bg='light green') # von MO
            hauptliste[geklickt][3]=1
        bei_mausklick(geklickt)           
    else:
        print ("292 ########## Element nicht vorhanden (ausserhalb der vordefinierten Parameter!):")

def new1_btn(state,reihe,spalte,tot,position):
    btn = Button() 
    btn.config(width=fenster_width, height=fenster_height,activebackground="yellow",activeforeground="black")
    position[str(btn)]=tot+1
    zw_text="FEHLT!"
    if tot < listenelemente_max_zeilen:
        zw_text=hauptliste[tot+1][1]+" " +str(hauptliste[tot+1][2])+" "+str(hauptliste[tot+1][4])+" "+hauptliste[tot+1][5]+" "+str(hauptliste[tot+1][6])
        if   hauptliste[tot+1][3]==0: btn.config (bg='brown')
        elif hauptliste[tot+1][3]==1: btn.config (bg='light green')
        else:
            btn.config (bg='#E1D1A9')               # farbe für den "Alles-Button"
    else:
        btn.state='disabled'               # damit wird Button ausgegraut
    btn.config (wraplength=fenster_width*8)        #das ist scheinbar in Pixel!?
    btn.config(text=zw_text,command = partial(toggle1, btn))  
    btn.state = bool(state)
    try:
        if hauptliste[tot+1][3]==1: btn.state = not btn.state # damit bei Erstaufbau die Buttondefinition (0/1) richtig ist
    except: print(" 99 ist ausserhalb!...also kein Problem")
    btn.grid(row=reihe,column=spalte,padx=4,pady=4)
    return btn
#####################################
    
########Definition des Fensters
main0= Tk() 
main0.title(matrixname)

for x in range (0,fenster_max_reihen):
    for y in range (0,fenster_max_spalten):
        z=x*fenster_max_spalten+y
        new1_btn(main0,x,y,z,position_dir)

main0.mainloop()
sys.exit()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast "btn". Benutz das doch, statt darum unnoetigerweise str(btn) aufzurufen. Was auch immer du dann mit position machen kannst und willst, du hast als Schluessel die Buttons und kannst die auch ansprechen dadurch.
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

@msmn: Sternchenimporte sind schlecht. Normalerweise importiert man `import tkinter as tk` und spricht alle tk-Namen per tk.xyz an.

Die Namensgebung ist nicht optimal. Irgendwelche Zahlen an Variablennamen zu hängen, ist meist keine gute Idee, was soll `main0` denn sein? Oder toggle1? Oder new1?
`hauptliste` list zum einen ein schlechter Name, gibt es auch noch neben-Listen? Zum anderen sollte keine Datentypen in Namen vorkommen. Der Inhalt ist auch schlecht. Was soll denn das erste Element der Liste? Der Kommentar was die Einträge bedeuten? Statt Listen in Listen zu packen, wo jeder der 7 Elemente eine bestimmte Bedeutung hat, solltest Du eine passendere Datenstruktur wählen, Wörterbuch, NamedTuple oder spezielle Klasse. Damit wird auch der Rest des Codes lesbarer.

Nach jedem Doppelpunkt der einen Block einleitet, wird eine neue Zeile angefangen. Aller Code auf Modulebene, der keine Funktionsdefinition oder Konstante ist, sollte in eine Funktion wandern.

In new1_btn übergibst Du statt `state` `main0`. Das macht so keinen Sinn. position ist eine seltsame Struktur. Statt den Index der Stringrepräsentation der Buttonobjekte in einem Wörterbuch zu speichern, solltest Du die gesamte Information, die Du brauchst, in einem Button-Objekt speichern und das benutzen.

Statt über Reihe und Spalte zu iterieren, wäre es einfacher über alle Elemente der Hauptliste zu iterieren und deren Index in Reihe und Spalte umzurechnen.
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@_deets_: Danke, habe schon die "str" weggenommen....

@Sirius3: ebenfalls danke....werde Deine Tipps einen nach den anderen umsetzen.
Du schreibst jedoch auch: "...Statt den Index der Stringrepräsentation der Buttonobjekte in einem Wörterbuch zu speichern, solltest Du die gesamte Information, die Du brauchst, in einem Button-Objekt speichern und das benutzen..:"
ich denke, genau das ist der Punkt...kannst Du bitte erklären, wie...."die gesamte Information in einem Button-Objekt gespeichert und benutzt werden kann"?
Danke

msmn
Antworten