【问题标题】:Tkinter widget after method方法后的 Tkinter 小部件
【发布时间】:2018-07-17 18:07:05
【问题描述】:

我还没有找到一个帖子来回答我的问题,所以:

我的 GUI 包含 3 个单独的“窗口”(框架?)作为类:TextInput、TextOutput 和 Statistic。出于性能原因,我只运行了 Statistic.mainloop() 但其他类也出现了。

我希望我的 TextInput 类做的是 遍历带有字符串的列表并将它们插入到文本小部件“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)

after() 方法插入“self.reference”的所有字符串,而不是预期的 FOR 循环。此外,整个 TextInput 应用程序似乎倾斜(递归太多?) 在另一个版本中,我尝试调用

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

在 1500 毫秒后再次将所有文本放入小部件中。

我做错了什么? 是我只跑的问题吗

Statistik.mainloop()

在底部而不是 TextInput.mainloop()。 感谢您的帮助

至于最小的例子:

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()

【问题讨论】:

  • 你为什么在 for 循环中使用after()。循环已经完成了所有的工作。为什么需要after()
  • 你需要使用after而不是一个for循环,而不是除了。如果您需要帮助实现,您需要向我们展示minimal reproducible example,我们可以测试。
  • 谢谢。 after() 的原因是逐行显示,中间有 1500 毫秒的中断。然后重新启动循环,直到一个按钮停止该过程。我应该把最小的例子放在哪里?
  • FWIW,在程序的整个生命周期内,您应该只调用一次 mainloop,而不是每帧一次。

标签: python python-3.x for-loop tkinter text-widget


【解决方案1】:

如果您的目标是遍历列表,以 1500 毫秒的间隔显示每一行,最简单的方法是使用一个函数进行一次迭代,然后使用 after 重复自身

可能是这样的:

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)

然后,只调用一次这个函数来启动进程:

self.read_ref(self, self.reference)

如果你希望能够停止它,你可以在你的函数中检查一个标志:

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

上面的代码会慢慢从self.reference 中删除项目。如果您不希望发生这种情况,请在启动时传递self.reference 的副本,以便该函数将从原始数据的副本中删除项目。

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

【讨论】:

  • 哇,谢谢。有用!如果我想重新开始,我可以这样做: else: self.after(1500, self.read_ref, self.reference[:])
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 1970-01-01
  • 2015-05-14
  • 2017-07-17
  • 1970-01-01
  • 2021-03-11
相关资源
最近更新 更多