简体   繁体   中英

Tkinter widget after method

I haven't found a thread to answer my question, so:

My GUI consists of 3 separate "windows" (Frames?) as classes: TextInput, TextOutput and Statistic. For performance reasons I only ran the Statistic.mainloop() but the other classes show up, too.

What I want my TextInput class to do is iterate through a list with strings and insert them in a textwidget "self.ref_text".

class TextInput(tk.Frame):

LARGE_FONT = ("Arial Bold ", 18)
SMALL_FONT = ("Arial", 16)
BGC = '#CDCDC1'
FG = ['#000000', '#f44242']

def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent)
    # print('415 TextInput instance: ', type(self))
    self.controller = controller
    self.reference = [] # some strings in here 
    self.createText()

def createText(self):
    self.ref_text = tk.Text(self, height=30, width=100, font=TextInput.SMALL_FONT)
    self.ref_text.config(background=TextInput.BGC)
    self.ref_text.grid(column=0, columnspan=4, row=1, padx=5, sticky="W")

def display_ref(self, line):
    print('line: ', line)
    self.ref_text.insert('end', line)

def read_ref(self):
    for line in self.reference:
        self.ref_text.insert('end', line)
        self.ref_text.after(1500, self.read_ref)

the after() method inserts all strings of "self.reference" instead of the intended FOR Loop. Also, the whole TextInput app seems to tilt (to much recursion?) In another version I tried to call

self.ref_text.after(1500, self.display_ref, line)

which again puts all the text in the widget after 1500 ms.

What am I doing wrong? Is it a problem that I only run

Statistik.mainloop()

at the bottom instead of TextInput.mainloop(). Thanks for your help

as for the minimal example:

import tkinter as tk

class Interface(tk.Tk):
def __init__(self, name, page,  *kwargs):
    tk.Tk.__init__(self, name, *kwargs)
    container = tk.Frame(self)
    container.pack(side="top", fill="both", expand=True)

    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)
    container.master.title(name)

    self.frames = {}
    self.windows = {}
    self.windows[name] = page

    self.window = page(container, self)
    self.frames[name] = self.window
    self.window.grid(row=0, column=0, sticky='nsew')

    self.show_window(name)

def show_window(self, cont):
    window = self.frames[cont]
    window.tkraise()

class TextInput(tk.Frame):

def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent)
    self.reference = ['a is a sentence', 'b follows a', 'c closes the session']
    self.createText()
    self.read = True
    self.read_ref()

def stop_read(self):
    self.read = False

def createText(self):
    self.ref_text = tk.Text(self, height=30, width=80,)
    self.ref_text.grid(column=0, row=1, columnspan=3, padx=5, sticky="W")

def display_ref(self, line):
    print('line: ', line)
    self.ref_text.insert('end', line)

def read_ref(self):
    '''
    the goal is to loop through self.reference line by line
    with a 1500 ms pause inbetween
    '''
    for line in self.reference:
        if self.read:
            self.ref_text.insert('end', line + '\n')
            self.ref_text.after(1500, self.read_ref)
        else:
            return

class Statistik(tk.Frame):
def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent)
'''does some other stuff'''

textinput_instance = Interface('TextInput', TextInput)
statistik_instance = Interface('Statistik', Statistik)

statistik_instance.mainloop()

If your goal is to loop through the list, displaying each line at 1500ms intervals, the easiest way is to have a function that does one iteration and then repeats itself using after

Something like this, perhaps:

def read_ref(self, lines):
    # remove one item from the list
    line = lines.pop(0)

    # insert it
    self.ref_text.insert("end", line + "\n")

    # run again in 1500ms if there's still more work to do
    if lines:
        self.after(1500, self.read_ref, lines)

Then, call this function exactly once to start the process:

self.read_ref(self, self.reference)

If you want to be able to stop it, you can check for a flag in your function:

def read_ref(self):
    ...
    if self.reference and not self.stop:
        self.after(1500, self.read_ref)

The above code slowly removes items from self.reference . If you don't want that to happen, pass a copy of self.reference when you start so that the function will remove items from a copy of the original data.

self.read_ref(self, self.reference[:])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM