dynamisch erzeugte Checkbuttons ausgrauen

Fragen zu Tkinter.
Antworten
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Hallo,
ich würde gerne die 'Decision'-Checkbuttons ausgrauen, wenn die oberste Checkbox des zugehörigen Frames deaktiviert wird. Das ganze soll natürlich reversibel vom Status der der oberen Checkbox abhängig sein. Ich denke mal es sollte mit 'command' und 'state' funktionieren, zB unter Nutzung der Funktion 'chg_moduleState' ?

Code: Alles auswählen

from Tkinter import *
from functools import partial

prioKey = 'prio'
        
def exDict(dict_):
    dict_ = dict_.copy()
    for key in dict_.keys():
        if not isinstance(dict_[key], dict):
            del dict_[key]
    return dict_

def prioList(dict_):
    for key in dict_.keys():
        if int(dict_[key][prioKey]) == 0:
            del dict_[key]
        list = sorted(dict_, key = lambda k: int(dict_[k][prioKey]))
    return list
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
def initWidgets(moduleTop):
    myDict = {'Frame_1': {'Decision_1': {'prio': '0'}, 'prio': '0', 'Decision_2': {'prio': '0'}, 'Decision_3': {'prio': '2'}}, 
                  'Frame_2': {'Decision_1': {'prio': '0'}, 'prio': '0', 'Decision_2': {'prio': '0'}, 'Decision_3': {'prio': '2'}}}
                                         
    def chg_moduleState(moduleFrame):
        if int_var.get() == 1:
            moduleFrame.moduleStateVar = ACTIVE
        else:
            moduleFrame.moduleStateVar = DISABLED
         
    for i, (moduleKey, moduleVal) in enumerate(myDict.iteritems()):
        setattr(moduleTop, moduleKey, LabelFrame(moduleTop, text = moduleKey, padx = 5, pady = 5))
        moduleFrame = getattr(moduleTop, moduleKey)
        moduleFrame.grid(sticky = 'NESW', row = 0, column = i)
        
        int_var = IntVar()
        int_var.set(int(moduleVal[prioKey]))
            
        setattr(moduleFrame, moduleKey, int_var)
        checkBtn = Checkbutton(moduleFrame, variable = int_var)#, command = None)
        checkBtn.grid(sticky = W, row = 0, column = 0, columnspan = 1)
        
        module = exDict(myDict[moduleKey])
        for j, (testKey, testVal) in enumerate(module.iteritems()):
            int_var = IntVar()
            int_var.set(int(testVal[prioKey]))
            setattr(moduleFrame, testKey, int_var)
            checkBtn = Checkbutton(moduleFrame, text = testKey, variable = int_var)#, state = ACTIVE)
            checkBtn.grid(sticky = W, row = j + 1, column = 0, columnspan = 1)
            
root = Tk()
initWidgets(root)
root.mainloop()
BlackJack

@hypnoticum: Ich bin mir immer noch nicht sicher ob Du `gettattr()` und `setattr()` nicht für etwas missbrauchst was wirklich besser in ein Dictionary gehört.

Die `exDict()` kann man deutlich vereinfachen wenn man nicht das Wörterbuch kopiert und dann die unerwünschten Elemente entfernt, sondern gleich ein neues Wörterbuch erstellt, das nur die gewünschten Elemente enthält:

Code: Alles auswählen

def ex_dict(dict_):
    return dict((k, v) for k, v in dict_.iteritems() if isinstance(v, dict))
Wenn ich mir den Verwendungszweck davon anschaue, dann sieht das aber so aus, als wenn es ein notwendiger Hack ist, weil die Datenstruktur ungünstig ist. Da werden bei den Datentypen in den Werten für "Frames" verschiedene Typen vermischt. Ein "Frame" scheint "Decisions" und eine Priorität zu besitzen. Da sollten also auch nur zwei Elemente einem "Frame" zugeordnet werden. Dann muss man nicht alle prüfen ob sie denn den richtigen Typ haben. Typprüfungen sollte man vermeiden. Die "Decisions" würde ich auch nicht in einem Wörterbuch speichern. Du sortierst zwar nach dem "prio"-Wert, aber es gibt ja auch "Decisions" mit der gleichen Priorität -- da werden die Checkboxen dann wieder innherhalb der Sortierung in zufälliger Reihenfolge angezeigt.

Das `myDict` könnte einen besseren Namen vertragen und ich würde die Daten deutlicher Formatieren, damit man die Struktur besser sieht.

Wenn eine "Frame"-Checkbox verändert wird, musst Du Code aufrufen lassen, der in einer Schleife den Status aller zugehörigen "Decision"-Checkboxen entsprechend ändert. Wenn Du da noch irgendwie den Überblick behalten möchtest, solltest Du so ein Frame in eine eigene Widget-Klasse kapseln.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

@BlackJack:
also gibt es keine bzw nur umständliche Lösungen das 'state'-Attribut bei der Erzeugung der Checkbuttons von einer entsprechende Variable 'moduleStateVar' abhängig zu machen, welche wiederum zur Laufzeit verändert wird?
Zur Struktur der Daten kann ich sagen, daß ich (als Anfänger) lange gebraucht habe und mit der Lösung eingentlich recht zufrieden bin. Die von mir erzeugte Parent-Klasse liest beliebig tief geschachtelte Parameter als Dictionaries ein. Bis zur Interpretation der Werte sind diese nur als Strings vorhanden.
Ich schätze deinen Rat kann aber oft nicht ganz nachvollziehen was du meinst. Ein paar Zeilen Code wären hilfreich.
Ich hatte das Codebeispiel eingestellt, damit Problem und Lösung einfach nachvollzogen werden können.
Den Bezeichner 'myDict' verwende ich nur in diesem Code-Beispiel.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

hat ne weile gedauert ...

Code: Alles auswählen

from Tkinter import *
from functools import partial

prioKey = 'prio'
        
def exDict(dict_):
    dict_ = dict_.copy()
    for key in dict_.keys():
        if not isinstance(dict_[key], dict):
            del dict_[key]
    return dict_

def prioList(dict_):
    for key in dict_.keys():
        if int(dict_[key][prioKey]) == 0:
            del dict_[key]
        list = sorted(dict_, key = lambda k: int(dict_[k][prioKey]))
    return list
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
def initWidgets(moduleTop):
    myDict = {'Frame_1': {'prio': '0', 'Decision_1': {'prio': '1'}, 'Decision_2': {'prio': '2'}, 'Decision_3': {'prio': '3'}}, 'Frame_2': {'prio': '0', 'Decision_1': {'prio': '1'}, 'Decision_2': {'prio': '0'}, 'Decision_3': {'prio': '6'}}}
                                         
    def chg_moduleState(modKey):
        F = getattr(moduleTop, modKey)
        int_var = getattr(F, 'modChkBtnVar')
        module = exDict(myDict[modKey])
        if int_var.get() == 1:
            for j in range(len(module)):
                chkBtn = getattr(F, 'testChkBtn' + str(j))
                chkBtn.configure(state = NORMAL)
        else:
            for j in range(len(module)):
                chkBtn = getattr(F, 'testChkBtn' + str(j))
                chkBtn.configure(state = DISABLED)
         
    for i, (moduleKey, moduleVal) in enumerate(myDict.iteritems()):
        
        setattr(moduleTop, moduleKey, LabelFrame(moduleTop, text = moduleKey, padx = 5, pady = 5))
        moduleFrame = getattr(moduleTop, moduleKey)
        moduleFrame.grid(sticky = 'NESW', row = 0, column = i)
       
        int_var = IntVar()
        int_var.set(int(moduleVal[prioKey]))  
        setattr(moduleFrame, 'modChkBtnVar', int_var)
        
        setattr(moduleFrame, 'modChkBtn', Checkbutton(moduleFrame, variable = int_var, command = partial(chg_moduleState, modKey = moduleKey)))
        checkBtn = getattr(moduleFrame, 'modChkBtn')
        checkBtn.grid(sticky = W, row = 0, column = 0, columnspan = 1)
        
        prio_var = StringVar() 
        prio_var.set(moduleVal[prioKey])
        setattr(moduleFrame, 'modEntryBxVar', prio_var)
        
        setattr(moduleFrame, 'modEntryBx', Entry(moduleFrame, textvariable = prio_var, width = 2 ))
        entryBx = getattr(moduleFrame, 'modEntryBx')
        entryBx.grid(sticky = W, row = 0, column = 1, columnspan = 1)
        
        module = exDict(myDict[moduleKey])
        for j, (testKey, testVal) in enumerate(module.iteritems()):
            int_var = IntVar()
            int_var.set(int(bool(int(testVal[prioKey]))))
            setattr(moduleFrame, 'testChkBtnVar' + str(j), int_var)
            
            setattr(moduleFrame, 'testChkBtn' + str(j), Checkbutton(moduleFrame, text = testKey, variable = int_var))
            checkBtn = getattr(moduleFrame, 'testChkBtn' + str(j))
            checkBtn.grid(sticky = W, row = j + 1, column = 0, columnspan = 1)
                       
            prio_var = StringVar() 
            prio_var.set(testVal[prioKey])
            setattr(moduleFrame, 'testEntryBxVar' + str(j), prio_var)
            
            setattr(moduleFrame, 'testEntryBx' + str(j), Entry(moduleFrame, textvariable = prio_var, width = 2 ))
            entryBx = getattr(moduleFrame, 'testEntryBx' + str(j))
            entryBx.grid(sticky = W, row = j + 1, column = 1, columnspan = 1)
        
        chg_moduleState(moduleKey)  
            
root = Tk()
initWidgets(root)
root.mainloop()
BlackJack

@hypnoticum: Um $GOTTES Willen -- jetzt fängst Du auch noch an dynamisch durchnummerierte Namen dynamisch als Attribute an Objekte zu binden und dynamisch wieder abzufragen. :shock: Das ist ja *total krank*. Jetzt bin ich mir ziemlich sicher das schon die `setattr()`/`getattr()`-Verwendung Murks war. Es gibt Dictionaries und Listen!

So einen "Frame" mit "Decisions" als Widget in einer Klasse zu kapseln *ist* eine Lösung und die ist auch nicht umständlich. Ganz im Gegensatz zu dem was Du da fabrizierst.

Zur Datenstruktur: Die könnte man zum Beispiel so schreiben ohne das man auf der selben Ebene Werte über ihren Typ unterscheiden muss:

Code: Alles auswählen

    data = {
        'Frame_1': {
            'decisions': [
                ('Decision_1', {'prio': '0'}),
                ('Decision_2', {'prio': '0'}),
                ('Decision_3', {'prio': '2'}),
            ],
            'prio': '0',
        },
        'Frame_2': {
            'decisions': [
                ('Decision_1', {'prio': '0'}),
                ('Decision_2', {'prio': '0'}),
                ('Decision_3', {'prio': '2'}),
            ],
            'prio': '0',
        }
    }
Anstelle der Tupel könnte man auch den Entscheidungsbezeichner mit in das Dictionary hinein ziehen: ``{'name': 'Decision_1', 'prio': '0'}``. Warum sind die Prioritäten eigentlich Zeichenketten wenn sie doch eigentlich als Zahlen interpretiert werden sollen?
Antworten