Button Farbe / Attribute ändern

Fragen zu Tkinter.
Antworten
UPler
User
Beiträge: 2
Registriert: Samstag 2. Januar 2016, 12:51

Hallo,

Python 3.4.3
Ich möchte mit diesem Programm aus der Liste L Positionen an- und abwählen (funktioniert)
Bei den angewählten Positionen sollen soll der Button die Farbe ändern(das will ned)

dieser <<<"..configure(bg = 'red')>>> Befehl funktioniert ohne OOP, (hab ich aus einem Tutorial)
hier aber ned.

Wie kann ich die Fenstergröße auf ein minimum begrenzen ?
Wie kann ich die Fenstergröße auslesen, um diese Auswahl an der rechten Fenstergrenze anzuhängen ?

Code: Alles auswählen

from tkinter import *

L = ['eins','Zwoa','Tres','viere','Fünef','Sixtin','SiebenTausendundsieben']
startX = 120
startY = 90
abstY = 30
Zielliste = []

class Anzahl():
    anzahl = 0

class Knopf(Anzahl):
    # Initialisieren abh.startX, startY, abstY
    def __init__(self, name, px, py):
        self.name = name
        self.px = px
        self.py = Anzahl.anzahl * abstY + py
        Anzahl.anzahl += 1
        Ausw = Button(text=self.name, command=self.Meldung).place(x=self.px, y=self.py, width=40, height=25)
        global Zielliste

    def Meldung(self):
        if self.name in Zielliste:
            #Name entfernen
            Zielliste.remove(self.name)
            print(self.name,' entfernt')
            
        else:
            #Name hinzu
            Zielliste.append(self.name)
            print(self.name,' dazu')
            self.Knopf.configure(bg = 'red')
            
            
# GUI-Objekte
# Fenster
tkFenster = Tk()
tkFenster.title('Zähler')
tkFenster.geometry('170x300')

for i in L:
    Ausw = Knopf(i, startX, startY)

#print(Ausw.__dict__)
    

# Aktivierung des Fensters
tkFenster.mainloop()
Danke,

mfg UPler
Zuletzt geändert von Anonymous am Samstag 2. Januar 2016, 13:22, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@UPler: Mit OOP funktioniert die `configure()`-Methode auch, allerdings muss man dafür OOP auch richtig machen. Wo denkst Du denn das ein `Knopf`-Attribut definiert wird was Du dann in der `Meldung()`-Methode versuchst zu verwenden?

Insgesamt solltest Du Dir OOP noch mal Grundlegend anschauen. Die `Anzahl`-Klasse ist im Grunde falsch. Vererbung ist eine „ist-ein(e)“-Beziehung und ein `Knopf` *ist* ganz sicher keine `Anzahl`. Das macht keinen Sinn. Das zählen von Exemplaren in einem Klassenattribut ist auch etwas das man eigentlich nicht machen sollte weil das im Grunde eine globale Variable ist. Womit wir bei ``global`` wären: Das hat dort wo es steht sowieso keinen Effekt auf das Programm, also weg damit, aber bitte dann auch gleich weg mit Variablen von der Modulebene. Dort sollten nur Konstanten, Funktionen, und Klassen definiert werden. Das Hauptprogramm gehört auch in eine Funktion. Sonst wird es sehr schnell sehr unübersichtlich welcher Code welche Auswirkungen hat.

Ebenfalls unübersichtlich sind Sternchenimporte. Gerade bei Tk holt man sich da hunderte von Namen ins Modul die man gar nicht benötigt. Das Modul wird üblicherweise unter dem Namen `tk` importiert (``import tkinter as tk``).

`Knopf` ist IMHO die falsche Stelle einen eigene Klasse zu verwenden, denn eigentlich braucht man ja etwas um den Zustand von allen Schaltflächen zu verwalten. Auf Tk-Seite wäre ein `Checkbutton` auch die näherliegende Wahl. Dann braucht man sich das mit der Farbe nicht selber programmieren sondern setzt sie dort schon beim erstellen der Schaltfläche.

Das mit der Fenstergrösse funktioniert automatisch solange man nicht `place()` verwendet. Weswegen man nicht `place()` verwenden sollte. Nimm `pack()` und/oder `grid()` und gegebenfalls zusätzliche `Frame`\s als Container, da man die beiden nicht im gleichen Container-Widget mischen darf. Dann braucht man auch keine Fenstergrösse vorgeben und `Anzahl` wird ebenfalls überflüssig.

Edit: Nun habe ich fast das wichtigste(‽) vergessen: Style Guide for Python Code.
BlackJack

Code: Alles auswählen

import tkinter as tk


class ChoiceButtons(tk.Frame):

    def __init__(self, master, choices):
        tk.Frame.__init__(self, master)
        self.choices = choices
        self.buttons = list()
        for choice in choices:
            variable = tk.IntVar(self)
            button = tk.Checkbutton(
                self,
                text=choice,
                indicatoron=False,
                selectcolor='red',
                variable=variable,
            )
            button.variable = variable
            button.pack(side=tk.TOP, fill=tk.X)
            self.buttons.append(button)

    def get_selected(self):
        return [
            c for c, b in zip(self.choices, self.buttons) if b.variable.get()
        ]


def main():
    choices = [
        'Eins',
        'Zwoa',
        'Tres',
        'Viere',
        'Fünef',
        'Sixteen',
        'Siebentausendundsieben',
    ]
    root = tk.Tk()
    choice_buttons = ChoiceButtons(root, choices)
    choice_buttons.pack()
    tk.Button(
        root, text='Test', command=lambda: print(choice_buttons.get_selected())
    ).pack()
    root.mainloop()


if __name__ == '__main__':
    main()
UPler
User
Beiträge: 2
Registriert: Samstag 2. Januar 2016, 12:51

@BlackJack,

Danke für Deine Verbesserung. Ich habe Dein Beispiel durchgearbeitet. Wie von Dir treffend beschrieben habe ich mit OOP Probleme.(in GW Basic und TurboPascal bin ich ohne ausgekommen). Die Grid-Methode ist genau das was ich brauche.

Kernproblem in dem Beispiel

Code: Alles auswählen

def __init__(self, master, choices):
        tk.Frame.__init__(self, master)
        self.choices = choices
        self.buttons = list()
        for choice in choices:						
            variable = tk.IntVar(self)
            button = tk.Checkbutton(				# hier bekommen alle Elemente aus choices einen eigenen Checkbutton ?
                self,								# Zugriff auf dieses Widget über variable ?
                text=choice,						# die Funtion get_selected list den Zustand mit variable.get aus ?
                indicatoron=False,					# wie haut der Zugriff hin dass genau der richtige Checkbutton abgefragt 
                selectcolor='red',					# wird ohne Index ?
                variable=variable,
            )
            button.variable = variable
            button.pack(side=tk.TOP, fill=tk.X)
            self.buttons.append(button)				# hier werden die einzelnen Buttons in die Liste buttons angehängt ?
 
    def get_selected(self):
        return [
            c for c, b in zip(self.choices, self.buttons) if b.variable.get()		# hier wird ohne Schleife auf alle Schaltzustände zugegriffen 
        ]													# und in return zurückgegeben ? Der Ausdruck c for c, b ?
        													# die Zip-Funktion fasst 2 Listen zu einer zusammen ?
Wie gesagt ich habe noch ned kapiert wie man auf die einzelnen Buttons zugreift. Kannst Du mir hierzu geeignete Tutorials / Literatur empfehlen ?
Ich orientiere mich momentan an unzähligen Youtube-Videos und Einführung in Python3 / v. Bernd Klein
Besten Dank
UPler
Zuletzt geändert von Anonymous am Montag 4. Januar 2016, 15:41, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@UPler: Jedes Element aus `choices` bekommt ein eigenes `IntVar` und ein eigenes `Checkbutton`-Objekt. Das später auf das jeweils richtige `IntVar`-Objekt zugegriffen wird liegt daran das beide Listen, Auswahlmöglichkeiten und Schaltflächen, gleich lang sind und die Elemente jeweils paarweise zusammengehören und mit der `zip()`-Funktion eben genau zu diesen Paaren zusammengeführt werden.

Was die tut ist dokumentiert und das kann man auch schnell mal in einer interaktiven Python-Shell ausprobieren:

Code: Alles auswählen

In [1]: A = [1, 2, 3]

In [2]: B = ['a', 'b', 'c']

In [3]: zip(A, B)
Out[3]: [(1, 'a'), (2, 'b'), (3, 'c')]
Der Ausdruck ``c for c, b`` ist kein vollständiger Ausdruck. Der Ausdruck besteht aus allem in den eckigen Klammern nach dem ``return``, inklusive dieser eckigen Klammern, und ist eine „list comprehension“. Die sollte in einem Python-Grundlagentutorial oder -Buch vorkommen. Der Ausdruck erstellt eine Liste und ist eine kompaktere Schreibweise für eine ``for``-Schleife in der vor der Schleife eine leere Liste erstellt wird, und in der Schleife dann über einen Ausdruck Elemente berechnet werden die an diese Liste angehängt werden. Optional mit einer Bedingung an die Eingangsdaten ob ein Element erzeugt werden soll oder nicht. Um das Beispiel von oben fortzuführen, wenn man nur die Elemente aus `B` haben möchte bei denen das entsprechende Element aus `A` ungerade ist (``%`` ist der Modulo-Operator (``MOD`` in Pascal)):

Code: Alles auswählen

In [4]: [x for x in zip(A, B)]
Out[4]: [(1, 'a'), (2, 'b'), (3, 'c')]

In [5]: [b for a, b in zip(A, B)]
Out[5]: ['a', 'b', 'c']

In [6]: [b for a, b in zip(A, B) if a % 2 != 0]
Out[6]: ['a', 'c']
In TurboPascal wirst Du wahrscheinlich nicht ohne den Verbundtyp ``Record`` ausgekommen sein. Stell Dir Klassen als ``Record``\s vor und Objekte als Exemplare von diesen ``Record``\s die dynamisch angelegt werden, also hinter den Kulissen als Zeiger herumgereicht werden. Die `__init__()` ist die ”Funktion” in der den Record-Feldern beim erstellen Werte zugeordnet werden. `self` ist immer der Zeiger auf den ``Record``-Wert. Letztendlich hast Du sehr wahrscheinlich auch in Pascal zumindest in Ansätzen objektorientiert programmiert oder objektorientierte Bibliotheken verwendet, ohne dass das so genannt wurde. OOP ist ja keine Spracheigenschaft sondern erst einmal nur ein Konzept. Das einfacher wird wenn die Sprache dafür explizite Unterstützung bietet, aber auch in anderen Sprachen zumindest bis zu einem gewissen Grad umgesetzt werden kann.
Antworten