text in tkinter.text_box im loop hinzufügen

Fragen zu Tkinter.
Antworten
ritti1988
User
Beiträge: 2
Registriert: Donnerstag 23. Juni 2022, 21:50

Hallo Zusammen,

in meiner GUI ist in der class Counter ein loop implementiert der durch das klicken auf den STOP-button beendet wird.
In dem loop soll zum Text in der tkinter.text_box ein weiterer String hinzugefügt werden, aber genau da liegt das Problem.

Es wird nur der "start_string" in der text_box angezeigt, und der bleibt unverändert.
Kann mir jemand helfen?
 
Anbei mein Code, bei dem aber wie beschrieben, sich der Text nicht ändern: 

Code: Alles auswählen

import tkinter as tk
           
class Counter():
            '''Periodically print counter value to stdout'''
            def __init__(self, parent, start=0, interval=1):
                        self.parent = parent
                        self.start_value = start
                        self.interval = interval
                        self.value = start
                       
                        # auto start
                        self.running = True
                        self.doit()
           
            def stop(self):
                        '''Stop counter'''
                        self.running = False
           
            def doit(self):
                        '''Called periodically to incrementer counter and print value'''
                        if self.running:
                                   self.value += 1
                                   text_box = get_text_box()
                                   text_box.insert('end', str(self.value))
                                   if self.running:
                                               self.parent.after(self.interval, self.doit)
           
root = tk.Tk()
root.title("Programm title")
root.geometry('700x300')
root.config(bg='#84BF04')
 
# Text Box
start_string = '#'*30 + '\n'
start_string += '-'*12 + ' START programm ' + '-'*12
text_box = tk.Text(root, height=12, width=80)
def get_text_box():
            return text_box
 
 
counter = Counter(root)
           
# Function button_stop
def button_stop():
            counter.stop()
           
# Button STOP
button_stop =  tk.Button(root, text = "STOP", padx=250, pady=20, command=button_stop)
button_stop.grid(row=2,column=0)
button_stop.pack(side='bottom')
 
# settings text_box
text_box.pack(expand=True)
text_box.insert('end', start_string)
text_box.config(state='disabled')
           
root.mainloop()
 
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ritti1988: Das Problem ist das DISABLED, weswegen ein `insert()` keine Wirkung hat.

Anmerkungen zum Quelltext: Eingerückt wird vier Leerzeichen pro Ebene, nicht 12.

Das auf Modulebene eine Funktion `button_stop` definiert wird, die kurz danach durch ein `Button`-Objekt ersetzt wird, ist verwirrend. Man sollte den gleichen Namen im gleichen Namensraum immer nur für Werte von *einen* (Duck-)Typ verwenden.

Die Funktion ist auch überflüssig, denn sie bekommt keine Argumente und ruft nur `counter.stop()` auf, also kann man also `command` auch gleich `counter.stop` übergeben, ohne den Umweg über eine zusätzliche Funktion.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Damit kann `get_text_box()` nicht mehr auf das vorher globale `text_box`-Objekt zugreifen. Alles was Funktionen und Methoden ausser Konstanten benötigen, bekommen sie als Argument(e) übergeben. Damit wird `get_text_box()` sinnlos, denn es würde nur etwas zurückgeben was der Aufrufer vorher übergeben hat.

Namen sollten nicht so weit von dem Punkt definiert werden wo sie letztlich verwendet werden. `start_string` wird erst sehr viel später tatsächlich verwendet. Hier könnte man argumentieren, dass die Verwendung näher zur Definition rücken sollte. Eventuell würde ich das auch überhaupt nicht erst an einen Namen binden. Beziehungsweise das erstellen des Widgets hinter das erstellen des Buttons verschieben, denn das entspricht dann eher der Reihenfolge in der die Elemente am Ende angezeigt werden.

Namen sollten die Worte in der richtigen Reihenfolge enthalten. `button_stop` ist was anderes als `stop_button`.

Entweder `grid()` oder `pack()`. Beides geht nicht mit ein und dem selben Widget. Man darf die nicht mal im gleichen Container-Widget mischen.

Wenn es für Zeichenketten mit einer festen Bedeutung eine Konstante im `tkinter`-Modul gibt, sollte man die verwenden, statt die literale Zeichenkette in den Quelltext zu schreiben.

`do_it()` braucht das `Text`-Objekt, das bedeutet das `Counter` das `Text`-Objekt braucht. Dafür kann dann `parent` wegfallen.

Das `start_value`-Attribut wird nirgends verwendet.

Das ``if self.running:`` *in* einem ``if self.running:`` ist überflüssig. Wenn man in dem Zweig ist, weiss man ja bereits, dass `self.running` wahr ist.

Ich würde da aber kein Flag für verwenden, sondern die ID vom `after()` merken und mit `stop()` einfach den `after()`-Job abbrechen.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk


class Counter:
    """
    Periodically print counter value to stdout.
    """

    def __init__(self, text_widget, start=0, interval=1):
        self.text_widget = text_widget
        self.value = start
        self.interval = interval
        self.task_id = None
        self.do_it()

    def stop(self):
        """
        Stop counter.
        """
        if self.task_id:
            self.text_widget.after_cancel(self.task_id)
            self.task_id = None

    def do_it(self):
        """
        Called periodically to incrementer counter and print value.
        """
        self.value += 1
        self.text_widget["state"] = tk.NORMAL
        self.text_widget.insert(tk.END, str(self.value))
        self.text_widget["state"] = tk.DISABLED
        self.task_id = self.text_widget.after(self.interval, self.do_it)


def main():
    root = tk.Tk()
    root.title("Programm title")
    root.config(bg="#84BF04")

    stop_button = tk.Button(root, text="STOP", padx=250, pady=20)
    stop_button.pack(side=tk.BOTTOM)

    text_box = tk.Text(root, height=12, width=80)
    text_box.insert(
        tk.END, "#" * 30 + "\n" + "-" * 12 + " START programm " + "-" * 12
    )
    text_box.config(state=tk.DISABLED)
    text_box.pack(expand=True)

    counter = Counter(text_box)
    stop_button["command"] = counter.stop

    root.mainloop()


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
ritti1988
User
Beiträge: 2
Registriert: Donnerstag 23. Juni 2022, 21:50

Vielen Dank für die extrem schnell und detaillierte Antwort.
Dein Quellcode funktioniert einwandfrei. Ich hab ihn direkt übernehmen können.

Auch werde ich in Zukunft versuchen Deine Anmerkungen, zur besseren Lesbarkeit meines Codes, umzusetzen.

Vielen Dank nochmal!
Antworten