Seite 1 von 1

Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Mittwoch 2. November 2022, 14:36
von joe2000
Hallo zusammen,

ich habe mir zu Übungszwecken folgende Aufgabe gestellt:
4 Checkbuttons sollen mit einem Eingabefeld gekoppelt werden, oder anders gesagt, die Checkbuttons und das Eingabefeld beeinflussen sich gegenseitig. Was ich hinbekommen habe, ist die eine Richtung: Werden die Checkbuttons in der Gui gesetzt, dann wird in das Eingabefeld das entsprechende Muster übertragen (0 bedeutet: Checkbutton nicht gesetzt / 1 bedeutet: Checkbutton gesetzt). Beispiel: Wird der erste und der letzte Checkbutton gesetzt, wird in das Eingabefeld eine 1001 übertragen.
Die andere Richtung bekomme ich nicht hin, wenn zum Beispiel in das Eingabefeld 1100 eingegeben wird, dann sollen die ersten beiden Checkbuttons gesetzt werden. Das gleiche Problem habe ich mit der Änderung des Textes der Checkbuttons (die Texte sollen nach Betätigung des Buttons geändert werden). Mir ist also nicht klar, wie man loop-generierte widgets ansprechen kann. Ich habe es mit Listen versucht, in die der Name des widgets geschrieben wird, aber auch hier mir fehlt mir wohl noch ein Schritt zum Erfolg.

Für einen einzelner Checkbutton (der aufgrund seines eindeutigen Namens direkt angesprochen werden kann), sind beide Richtungen kein Problem.

Unten stehend der Code dazu, für einen Tipp wäre ich dankbar (Fehleingaben über try/except noch nicht abgefangen). Und wenn Euch sonst noch was auffällt, was an dem Code nicht so dolle ist, über entsprechende Hinweise würde ich freuen. Ich stehe bzgl. Python noch am Anfang.

Schon mal Danke…

Code: Alles auswählen

import tkinter as tk
from functools import partial

NUMBER_CHECKBOX=4

class Gui(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        #entry
        self.entry_var= tk.StringVar()
        self.entry_var.trace('w', self.entry_change)
        self.entry = tk.Entry(self, textvariable=self.entry_var)
        self.entry.grid(sticky=tk.W, row=0, column=0)
        #label
        self.label = tk.Label(self)
        self.label.grid(sticky=tk.W, row=1, column=0)
        #checkboxen loopgeneriert
        self.list_checkbox_value=[]
        self.list_checkbox_text=[]
        for idx in range(NUMBER_CHECKBOX):
            self.checkbox_text_var = tk.StringVar()
            self.checkbox_text_var='Check_'+str(idx)
            self.list_checkbox_text.append(self.checkbox_text_var)
            self.checkbox_value_var = tk.IntVar()
            self.list_checkbox_value.append('0')
            self.checkbox = tk.Checkbutton(self,text=self.checkbox_text_var, variable=self.checkbox_value_var)
            self.checkbox.config(command=partial(self.checkbox_change, self.checkbox_text_var, self.checkbox_value_var))
            self.checkbox.grid(sticky=tk.W, row=2+idx, column=0)
        #button
        self.button = tk.Button(self,text='Ändere Text Checkboxen', command=self.checkbox_text_change)
        self.button.grid(sticky=tk.W, row=7, column=0)
        #einzelcheckbox 
        self.checkbox_single_value = tk.IntVar()
        self.checkbox_single_text = tk.StringVar()
        self.checkbox_single_text.set('Eingabefeld befüllt')
        self.checkbox_single = tk.Checkbutton(self,textvariable=self.checkbox_single_text, variable=self.checkbox_single_value, command=self.checkbox_single_change)
        self.checkbox_single.grid(sticky=tk.W, row=8, column=0)

    def entry_change(self,a, *args):
        self.label['text']=self.entry_var.get()
        if self.entry_var.get()=='':
            self.checkbox_single_value.set(0)
        else:
            self.checkbox_single_value.set(1)
        #self.checkbox_value.set(1)# ->So funkiert es nicht

    def checkbox_change(self, checkbox_text, checkbox_value):
        idx=int(self.list_checkbox_text.index(checkbox_text))
        self.list_checkbox_value[idx]=str(checkbox_value.get())
        anzeige_label=("".join(self.list_checkbox_value))
        if anzeige_label=='0000':
            anzeige_label='' 
        self.label['text']=anzeige_label
        self.entry_var.set(anzeige_label)
    
    def checkbox_single_change(self,*args):
        if self.checkbox_single_value.get()==0:
           self.entry_var.set('')

    def checkbox_text_change(self):
        self.checkbox_single_text.set ('neue Bezeichnung')
        #self.checkbox_text_var.set ('Check_loop')->So funkiert es nicht

def main():
    root = tk.Tk()
    root.title("test_2")
    root.geometry('150x200')
    gui = Gui(root)
    gui.pack()
    root.mainloop()


if __name__ == "__main__":
    main()

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Mittwoch 2. November 2022, 14:49
von Sirius3
Wenn man innerhalb einer for-Schleife Attribute setzt, dann macht man irgendwas falsch, denn diese Attribute werden ja im nächsten for-Schleifenlauf wieder überschrieben.
`checkbox_text_var` ist erst ein StringVar-Objekt dann ein String. In die Listen willst Du keine Strings stecken, sondern die Var-Objekte.
Var-Objekte sollten als erstes Argument auch den `master` bekommen.

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Mittwoch 2. November 2022, 16:00
von joe2000
@Sirius3: danke für die rasche Antwort, das Problem mit dem Überschreiben der Attribute in der Schleife habe ich verstanden. Beim Rest fehlen mir wohl noch ein paar Grundlagen, ich denke ich muss mich der Sache schrittweise nähern:
Ich habe nun die Zeile self.checkbox_text_var='Check_'+str(idx) entfernt und in der Liste steht dann: [<tkinter.StringVar object at 0x0000022D17F8B9D0>, <tkinter.StringVar object at 0x0000022D1840FD00>, <tkinter.StringVar object at 0x0000022D1840FE80>, <tkinter.StringVar object at 0x0000022D18468040>]. Das hast Du gemeint, oder?

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Mittwoch 2. November 2022, 22:03
von Sirius3
Das geht in die richtige Richtung.

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Donnerstag 3. November 2022, 17:55
von joe2000
Nach ewigem rumprobieren und recherchieren muss ich mir leider eingestehen, dass ich nicht weiterkomme. Geht das überhaupt so wie ich das möchte? Es ist mir klar, dass ich den Mustercode hier nicht geliefert bekomme, das ist ja auch nicht Sinn der Sache. Aber mir ist partout nicht klar, wie mit den Informationen aus der Liste die Checkbuttons ansprechen kann...

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Donnerstag 3. November 2022, 18:09
von __deets__
Ja, gehen tut das. Und die Checkbuttons sprichst du an, indem du deren Variable veraenderst.

Code: Alles auswählen

import tkinter as tk


class App:

    def __init__(self, root):
        self._checkbox_var = tk.IntVar()
        tk.Checkbutton(root, text="Influence me", variable=self._checkbox_var).pack()
        tk.Button(root, text="Set to one", command=lambda: self._set_checkbox(1)).pack()
        tk.Button(root, text="Set to zero", command=lambda: self._set_checkbox(0)).pack()

    def _set_checkbox(self, number):
        self._checkbox_var.set(number)

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Donnerstag 3. November 2022, 19:00
von joe2000
@deets: Danke für Deine Antwort. Leider löst das meine Problem nicht, dies ist nämlich die Adressierung eines einzelnen Checkbuttons der über eine Schleife generiert wurde. Wenn ich in Deinen Code eine Schleife implementiere (siehe unten), dann bekomme ich 4 Checkbuttons die nun nicht mehr singulär gesetzt werden können. Und wenn der Button betätigt wird, werden auch alle angesprochen. Mein Klemmer ist, dass ich schleifengenerierte Checkbuttons einzeln Ansprechen möchte. Ich glaube das muss ich irgendwie über Listen machen, aber genau da komme ich nicht weiter...

Code: Alles auswählen

import tkinter as tk


class App:

    def __init__(self, root):
        self._checkbox_var = tk.IntVar()
        for idx in range(4):
            tk.Checkbutton(root, text="Influence me", variable=self._checkbox_var).pack()
        
        tk.Button(root, text="Set to one", command=lambda: self._set_checkbox(1)).pack()
        tk.Button(root, text="Set to zero", command=lambda: self._set_checkbox(0)).pack()

    def _set_checkbox(self, number):
        self._checkbox_var.set(number)

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Donnerstag 3. November 2022, 20:20
von __deets__
Du musst schon eine Variable pro Checkbox machen. Darum wurde hier ja auch von einer Liste gesprochen.

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Donnerstag 3. November 2022, 20:40
von Sirius3
Du brauchst für jede Checkbox ein eigenes IntVar, und das muß in eine Liste.

Code: Alles auswählen

import tkinter as tk

NUMBER_CHECKBOX=4

class Gui(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.label = tk.Label(self)
        self.label.grid(sticky=tk.W, row=1, column=0)
        self.checkbox_vars = []
        for idx in range(NUMBER_CHECKBOX):
            var = tk.IntVar(self, 0)
            var.trace('w', self.var_changed)
            checkbox = tk.Checkbutton(self, text=f"C{idx}", variable=var)
            checkbox.grid(sticky=tk.W, row=2+idx, column=0)
            self.checkbox_vars.append(var)

    def var_changed(self, *args):
        text = [f"{var.get()}"
            for var in self.checkbox_vars
        ]
        self.label['text'] = text

def main():
    root = tk.Tk()
    gui = Gui(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Freitag 4. November 2022, 10:29
von joe2000
@Sirius3: Weltklasse, genau der Kniff hat mir gefehlt, vielen Dank! Das war Deine gute Tat des Tages, somit bin ich nun auch in der Lage, jeden Checkbutton einzeln anzusprechen (wie im Beispiel unten über einen Button)

Was allerdings noch funktioniert, ist das ändern des Beschriftung ausgewählter Checkbuttons, da fehlt mir der Befehl an der Stelle an der im code gerade 'pass' steht.
Das ist zwar nicht ganz so wichtig, es würde mich allerdings schon interessieren wie das geht.

Code: Alles auswählen

import tkinter as tk

NUMBER_CHECKBOX=4

class Gui(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.label = tk.Label(self)
        self.label.grid(sticky=tk.W, row=1, column=0)
        self.checkbox_vars = []
        self.checkbox_text=[]
        for idx in range(NUMBER_CHECKBOX):
            var = tk.IntVar(self, 0)
            var.trace('w', self.var_changed)
            txt_var = tk.StringVar
            txt_var=f"C{idx}"
            checkbox = tk.Checkbutton(self, text=txt_var, variable=var)
            checkbox.grid(sticky=tk.W, row=2+idx, column=0)
            self.checkbox_vars.append(var)
            self.checkbox_text.append(txt_var)

        #button
        self.button = tk.Button(self,text='Ändere Wert Checkbox 2', command=self.checkbox_val_change)
        self.button.grid(sticky=tk.W, row=7, column=0)

    def var_changed(self, *args):
        text = [f"{var.get()}"
            for var in self.checkbox_vars
        ]
        self.label['text'] = text

    def checkbox_val_change(self, *args):
        idx=0
        for var in self.checkbox_vars:
            if idx==2:
                f"{var.set('1')}"
            idx+=1

        idx=0
        for txt in self.checkbox_text:
            if idx==2:
                pass
            idx+=1

def main():
    root = tk.Tk()
    gui = Gui(root)
    gui.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

Re: Ansprechen von widgets, die über eine loop generiert wurden

Verfasst: Freitag 4. November 2022, 10:50
von Sirius3
Jetzt hast Du ja wieder den selben Fehler mit StringVar drin, wie ganz am Anfang, aber das Prinzip ist das selbe wie mit IntVar. Das solltest Du übernehmen können.