Änderungssignal für tkinter.Text

Fragen zu Tkinter.
Antworten
Tonteria
User
Beiträge: 2
Registriert: Dienstag 16. Februar 2021, 04:31

Hallo allerseits,
bin noch relativ neu bei Python und auch bei tkinter (habe bisher hauptsächlich mit Delphi programmiert).
Vielleicht liegt es daran, dass ich nicht die richtigen Suchworte eingebe, vielleicht hab ich auch ansonsten ein Verständnisproblem oder sehe den Wald vor lauter Bäumen nicht.

Ich habe ein kleines Programm geschrieben, das ein tkinter.Text enthält. Darunter möchte ich ein Label setzen, das immer die Anzahl der Zeichen im Text anzeigt.
Text ist da, Label ist da. Funktioniert auch beides (Text ändern, Text abfragen, Textlänge bestimmen, Textlänge ins Label schreiben).
Im Moment mache ich das per Knopf, aber eigentlich hätte ich das gerne "live".

Nur wie bekomme ich mit, wenn sich am Inhalt des Text etwas ändert?
Auch akzeptabel wäre es, wenn ich zumindest den Fokusverlust des Textfeldes mitbekäme, aber auch da bin ich offenbar zu dusselig, um eine hilfreiche Suchanfrage zu formulieren.
(Bei Delphi gibt es "onChange" und sowas, vermutlich hänge ich noch zu sehr an diesem System, um die richtigen Suchanfragen einzugeben. Bitte um Verzeihung!)

Danke für alle Hinweise!
Tonteria

Hier mein Minimal-Beispiel:

Code: Alles auswählen

import tkinter as tk
root = tk.Tk()

mytext = tk.Text(root,width=20,height=10)
mytext.pack(side=tk.TOP,expand=True,fill=tk.BOTH)

mylabel = tk.Label(text= "Number of signs: ??" )
mylabel.pack(side=tk.TOP,expand=True,fill=tk.X)

def countsigns():
    atext = mytext.get(1.0,tk.END)
    nsigns = len(atext)
    mylabel.config(text="Number of signs: " + str(nsigns))

countsigns()

mybutton = tk.Button(text="Count",command=countsigns)
mybutton.pack(side=tk.TOP,expand=True,fill=tk.X)

root.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tonteria: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Dadurch gibt es dann auch keine globalen Variablen mehr: Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigten als Argument(e) übergeben.

Bei so einem Minimalbeispiel reicht da noch `functools.partial()`, aber jede nicht-triviale GUI braucht am Ende objektorientierte Programmerung (OOP), also mindestens eine eigene Klasse.

Gewöhne Dir so einen Unsinn wie `my` (oder auch `a_`) überall vor zu klatschen gar nicht erst an, beziehungsweise ist das wohl etwas was Du Dir abgewöhnen musst, weil Du von einer Sprache kommst die nicht zwischen Gross und Kleinschreibung unterscheided. in Python sind `button` und `Button` zwei unterschiedliche Namen. Mal davon abgesehen wäre selbst wenn dem nicht so wäre in dem Programm keine Namenskollision weil der `Button`-Datentyp in einem anderen Namensraum steckt (`tk`) als der Name an den Du dann das Exemplar bindest.

Den Text beim erstellen des Label braucht man nicht angeben, der wird ja nie angezeigt sondern gleich darauf durch den tatsächlichen Text ersetzt.

„signs“ sind andere Zeichen als Du hier meinst, es sei denn der Benutzer soll in dem Textfeld tatsächlich nur Symbole/Schilder und nicht beliebige Unicode-Zeichen eingeben.

`Text`-Widgets kennen ein "<<Modified>>"-Ereignis:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial


def update_character_count(text_widget, label, _event=None):
    count = len(text_widget.get(1.0, tk.END))
    label["text"] = f"Number of characters: {count}"
    text_widget.edit_modified(False)


def main():
    root = tk.Tk()

    text_widget = tk.Text(root, width=20, height=10)
    text_widget.pack(side=tk.TOP, expand=True, fill=tk.BOTH)

    label = tk.Label()
    label.pack(side=tk.TOP, expand=True, fill=tk.X)

    update_character_count(text_widget, label)

    text_widget.bind(
        "<<Modified>>", partial(update_character_count, text_widget, label)
    )

    root.mainloop()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Tonteria
User
Beiträge: 2
Registriert: Dienstag 16. Februar 2021, 04:31

@__blackjack__: Danke für Deine schnelle und ausführliche Antwort! :)
Dann werde ich mich jetzt mal mit "<<Modified>>", functools und partial beschäftigen. 8)

So einen Unsinn wie "my" oder ähnliches mache ich nur bei Sachen, die ich ausprobiere - damit signalisiere ich mir selbst, dass die wieder raus müssen oder nochmal überarbeitet werden sollen , oder dass es sich um ein Minimalbeispiel handelt :wink: Normaler Weise hätte ich den Knopf wohl "button_count_now" genannt, das Label "label_number_characters" und das Text-Widget "text_write" (im tatsächlichen Programm gibt es zwei Text-Widgets: Eines in dem editiert werden kann ("text_write"), und das andere zeigt einen nicht editierbaren Text ("text_read") - in letzterem ist die Anzahl der Zeichen allerdings uninteressant :wink: )

Delphi ist, wenn mich nicht einiges in den letzten Jahren getäuscht hat, eine objektorientierte Sprache, insofern ist das nicht mein Problem. :wink: Das mit der Groß- und Kleinschreibungsunterscheidung kommt mir eher entgegen (mich hat es immer gestört, wenn Kollegen sich im Delphi-Code nicht auf eine Variante festlegen konnten). Bei Python muss ich mich vor allem dran gewöhnen, dass eine Variable ohne Einführung einfach gesetzt wird. Das ging in Delphi nämlich nicht und gab dann eine Fehlermeldung. Dass es in Python einfach eine neue Variable gibt, wenn man sich vertippt hat, und ich mich lange wundere, wieso die Änderung nicht da ankommt, wo ich sie haben möchte, finde ich viel gewöhnungsbedürftiger :roll:

Das "Characters" der üblichere Begriff ist, ist mir auch nicht neu, aber "Signs" ist kürzer und passte besser in das kleine Beispielprogrammfenster (ich steh nicht so auf Abkürzungen wie "chars." ;) ).
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tonteria: Gerade *weil* Delphi eine objektorientierte Programmiersprache ist gibt es dieses Klasse vs. Exemplar Namensproblem. Wenn man in Python eine Klasse hat und ein Exemplar davon erstellen will und keinen passenderen Namen hat, schreibt man in der Regel ``foo = Foo()``. Etwas was bei Pascal/Delphi nicht geht weil das dort der gleiche Name wäre und sich die Leute dann oft mit Vorsilben/Namenszusätzen wie `my`, `o`, `a`, und so weiter behelfen.

Das mit den Vertippern ist in modernen Editoren mit Autovervollständigung nicht so häufig und die meisten Fälle werden von statischen Lintern auch gefunden. Wenn Vertipper in Namen dann sind die heute eher konsequent überall gleich im Programm. 🙂

„Characters“ ist gegenüber „signs“ nicht der üblichere Begriff sondern der korrekte. „Signs“ ist in dem Kontext halt schlicht falsch. Die Argumentation mit der Fenstergrösse ist komisch weil der Text ja nicht in eine vorgegebene Fenstergrösse passen muss, sondern sich die Fenstergrösse am Inhalt, also am Text orientiert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten