Binding - Reaktion auf veränderte Werte in Entry

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 13. Juni 2018, 13:08

Hallo zusammen,

habe folgendes Problem.

Bei einer GUI mit tk, möchte ich einen bestimmten Button, zwischen 'state=normal' und 'state=disabled' verändern.
Dies allein stellt kein Problem dar.
Ich möchte jedoch von einem bestimmten Entry-Feld, bei Wert-Änderung, den Button steuern.
Ich denke da an binding, jedoch bin ich mir nicht sicher ob dies damit möglich ist.

Wenn es mit Binding funktionieren würde, was müsste statt der Fragezeichen stehen?

Code: Alles auswählen

self.entry_0.bind("<????>", self.button_config)
Wenn es mit Binding nicht geht, welche Optionen hätte ich dann?

Grüße Nobuddy
Benutzeravatar
__blackjack__
User
Beiträge: 204
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 13. Juni 2018, 13:12

Schau Dir stattdessen mal die `*Var`-Klassen von `tkinter`, deren `trace()`-Methode und das `textvariable`-Argument von `Entry.__init__()` an.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 13. Juni 2018, 13:26

@__blackjack__, Danke für den Tip, werde ich mir anschauen!
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 13. Juni 2018, 15:10

Das Einzige, was ich bis jetzt zu tkinter trace() gefunden habe ist dies https://docs.python.org/3.0/library/trace.html.
Ist wahrscheinlich nicht was Du meinst oder?

Habe eingesehen, dass das mit Binding kann nicht funktionieren kann, da das Entry-Feld Werte in der Regel automatisch erhält.
Benutzeravatar
__blackjack__
User
Beiträge: 204
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 13. Juni 2018, 16:08

Nee ich meine die `trace()`-Methode von `tkinter.Variable` beziehungsweise deren Unterklassen. Da ist in der Tat nicht so viel Information zu finden. Am meisten scheint Effbot zu haben: http://effbot.org/tkinterbook/variable.htm

Hier ist der Docstring:

Code: Alles auswählen

In [9]: tk.Variable.trace?
Signature: tk.Variable.trace(self, mode, callback)
Docstring:
Define a trace callback for the variable.

MODE is one of "r", "w", "u" for read, write, undefine.
CALLBACK must be a function which is called when
the variable is read, written or undefined.

Return the name of the callback.
File:      /usr/lib/python2.7/lib-tk/Tkinter.py
Type:      instancemethod
Was da nicht steht, ist das man auch 'rw' angeben kann wenn man lesen und schreiben in einer Rückruffunktion behandeln möchte.

Da ist dann ein wenig experimentieren und eventuell Tcl/Tk-Dokumentation lesen angesagt. In Tcl kann man übrigens jede Variable ”verfolgen”. Diesen Mechanismus kapselt `tkinter` mit dem `tkinter.Variable`-Datentyp.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Donnerstag 14. Juni 2018, 09:44

Das mit der tkinter.Variable und trace(), ist eine feine Sache.

Das von Dir anfangs beschriebene '`textvariable`-Argument von `Entry.__init__()`', ist mir noch nicht so klar.
Wenn das durch meine unteren Beispiele, sich nicht erledigt hat, wäre ein kurzes Beispiel zum Verständnis gut.

Bei mir sieht das so aus (kleiner Auszug):

Code: Alles auswählen

        # Kontrolle auf Veränderung, bei Spaltennamen.
        bc = dict()
        for name in ['bdatum', 'auftrag', 'datum', 'nummer']:
            try:
                bc[self.controller.head_names._fields.index(name)] = True
            except ValueError:
                pass
........

        for i in self.header[0]:
            entry = self.header[0][i]
            var = tk.StringVar()
            var.set(entry)
            try:
                # Controlling Entry auf Veränderung.
                bc[i]
                var.trace('w', self.button_control)
            except KeyError:
                pass
            data.append(var)
            tk.Entry(self.head_0E, width=width, bg=self.col5, fg=self.col1,
                font=self.txt2, text=var).grid(row=0, column=i, padx=px,
                pady=py, sticky=tk.NSEW)
Und bei Kontrolle von Checkbutton:

Code: Alles auswählen

        [int_var.trace('r', self.button_control)
            for int_var in self.check_vars]
Grüße Nobuddy
Benutzeravatar
__blackjack__
User
Beiträge: 204
Registriert: Samstag 2. Juni 2018, 10:21

Donnerstag 14. Juni 2018, 10:20

Falls `self.header[0]` ein Wörterbuch ist, kann man in der Schleife gleich über die Schlüssel/Wert-Paare iterieren und spart sich die erste Zeile in der Schleife.

`entry` ist ein irreführender Name für etwas das kein `Entry` ist, sondern der *Inhalt* eines `Entry`.

`try`/`except` und ein Schlüsselzugriff dessen Ergebnis gar nicht verwendet wird, ist ziemlich umständlich um heraus zu finden ob der Schlüssel existiert. Dafür gibt es den ``in``-Operator.

Und bei `Entry` muss dam `tk.Variable`-Exemplare wie schon gesagt über das `textvariable`-Argument übergeben. Bei `text` wird das was übergeben wurde einfach in eine Zeichenkette umgewandelt. Da steht dann am Ende so etwas wie 'PY_VAR0' im Eingabefeld.

Code: Alles auswählen

        for i, text in self.header[0].items():  # .iteritems() in Python 2
            var = tk.StringVar(value=text)
            if i in bc:
                var.trace('w', self.button_control)
            data.append(var)
            tk.Entry(self.head_0E, width=width, bg=self.col5, fg=self.col1,
                font=self.txt2, textvariable=var).grid(row=0, column=i, padx=px,
                pady=py, sticky=tk.NSEW)
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Sirius3
User
Beiträge: 7785
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 14. Juni 2018, 12:09

Wörterbücher zu nehmen, um darin einen Index, statt des beschreibenden Namens zu verwenden, ist etwas seltsam. Das ist bei bc so, und scheint auch bei self.header[0] so zu sein. Nimm direkt die Namen. Was soll der Index 0 bei header? gibt es davon mehrere? bc ist ein sehr schlechter Variabelname, weil er nichts aussagt; nicht mal aus dem Kontext wird mir klar, für was b und c stehen soll.

Was ist `self.controller.head_names` für ein Objekt? Auf Felder, die mit einem _ anfangen, sollte man nicht von außen zugreifen. Du hast eine fixe Anzahl an Begriffen, die aber nicht in header_names vorkommen können, oder was ist der Sinn, ValueError abzufangen?

Code: Alles auswählen

bc = dict.fromkeys({'bdatum', 'auftrag', 'datum', 'nummer'}.intersection(self.controller.head_names._fields), True)
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Donnerstag 14. Juni 2018, 14:57

Hallo zusammen,

self.header ist ein Wörterbuch mit Namen der Spalten, das aus namedtuple mit rechnungkopf._fields hergeleitet ist.
Der obere Teil meiner GUI, bestehend aus dem Rechnungskopf, ist in 3 Ebenen unterteilt. Diese Ebenen, habe ich zuvor self.header zugewiesen, daher z.B. self.header[0] für die erste Ebene.

@__blackjack__ , Danke für Dein Info zum Iterieren!
Habe dies gleich umgesetzt.

@Sirius3, self.controller.head_names ist ein namedtuple.
Bei bc, ist die Namensgebung schlecht, das gebe ich zu. Vielleicht wäre value2trace besser?
Das mit dem Abfangen des ValueErrors, hat den Hintergrund, da ich die GUI mehrfach nutze, z.B. für Lieferanten-Bestellung, -Wareneingang, Kunden-Bestellung, - Rechnung und der Header für die GUI ist unterschiedlich in der Namensgebung.

Dein Code-Vorschlag, kannte ich bisher noch nicht, Danke dafür!

Für weitere Vorschläge und Verbesserungen, habe ich immer ein offenes Ohr!

Grüße Nobuddy
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Donnerstag 14. Juni 2018, 15:07

@Sirius3, habe Deinen Code-Vorschlag folgendermaßen abgeändert:

Code: Alles auswählen

        trace_values = {'bdatum', 'auftrag', 'datum', 'nummer'}

        for i, text in self.header[0].items():
            var = tk.StringVar(value=text)
            dict.fromkeys(trace_values.intersection(
                self.controller.head_names._fields),
                var.trace('w', self.button_control))
Sirius3
User
Beiträge: 7785
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 14. Juni 2018, 16:16

@Nobuddy: das macht keinen Sinn. Du erzeugst ein Wörterbuch, das Du gleich wieder wegwirfst.
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Freitag 15. Juni 2018, 12:39

@Sirius3, mit Wörterbuch meinst Du 'trace_values'.
Das habe ich extra seperat am Anfang erstellt, um Änderungen zentral bewältigen zu können.

Aber warum werfe ich das Wörterbuch gleich wieder weg, das verstehe ich nicht.
Bitte erkläre mir das, vielleicht habe ich auch bei Deinem Code-Beispiel etwas falsch verstanden.
Benutzeravatar
__blackjack__
User
Beiträge: 204
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 15. Juni 2018, 12:46

Der `dict.fromkeys()`-Aufruf erstellt ein Wörterbuch — mit dem im Code dann aber gar nichts gemacht wird. Das wird erstellt und sofort wieder verworfen.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Freitag 15. Juni 2018, 15:38

Ok, dann habe ich wohl die Funktionsweise falsch verstanden.
Beim nochmaligen Überprüfen, hat sich das auch bestätigt.
Muss mir überlegen, wie sich das am Besten anwenden lässt.
Nobuddy
User
Beiträge: 743
Registriert: Montag 30. Januar 2012, 16:38

Freitag 15. Juni 2018, 17:10

Momentan, sehe ich jetzt für den Code-Vorschlag von Sirius3 keine Verwendung. Vielleicht, fehlt mir auch der richtige Blickwinkel?

Aus meiner jetzigen Sicht, ist dies wohl am einfachsten (Vorschlag __blackjack__):

Code: Alles auswählen

        # Berücksichtigung von Labels, zum trace-controlling.
        trace_values = {'BDATUM', 'AUFTRAG', 'DATUM', 'NUMMER'}

        for i, text in self.header[0].items():
            var = tk.StringVar(value=text)
            # trace-Controlling bei Übereinstimmung der Label.
            if self.header_names[0][i] in trace_values:
                var.trace('w', self.button_control)
Antworten