我遇到一个相当简单的应用程序的问题。 它运行正常,但我希望它执行得慢一些。
我们的想法是从列表中随机生成一个名称,显示它,然后在每次点击一个按钮时将其从列表中删除。
为了让它更有趣,我希望程序之前能显示几个名字 挑选最后一个。我为此使用了一个简单的for循环。但是,代码执行得如此之快,最后显示的唯一名称是最后一个。
使用time.sleep()
只会延迟显示姓氏。没有显示其他名字。
这是我的代码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from tkinter import *
import random
import time
class Application(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(Application, self).__init__(master)
self.grid()
self.name_list = ["Thorin","Tyler","Jose","Bryson","Joe"]
self.create_widget()
def create_widget(self):
self.lbl = Label(self)
self.lbl["text"] = "Click to spin"
self.lbl["font"] = ("Arial", 24)
self.lbl.grid()
self.bttn = Button(self)
self.bttn["text"]= "Spin"
self.bttn["command"] = self.spin
self.bttn.grid()
def spin(self):
if self.name_list:
for i in range(5):
index = random.randrange(len(self.name_list))
self.lbl["text"] = self.name_list[index]
self.lbl.grid()
self.name_list.pop(index)
else:
self.lbl["text"] = "No more names"
self.lbl.grid()
def main():
root = Tk()
root.title("Click Counter")
root.geometry("600x600")
app = Application(root)
root.mainloop()
if __name__ == '__main__':
main()
答案 0 :(得分:3)
这是与GUI编程相关的一类非常常见的问题。问题的核心是窗口绘图管理器。只要您的函数正在执行,绘图管理器就会被冻结;在您的功能结束之前,更新标签的文本将没有明显的效果。因此,如果你的for
循环中有一个sleep(1)
循环,它所做的就是冻结所有内容五秒钟,然后在函数最终结束时使用你的最终值进行更新。
解决方案是使用after
方法,它告诉Tkinter在将来的某个时刻调用指定的函数。与sleep
不同,这为绘图管理员提供了更新窗口所需的喘息空间。
一种可能的方法是使用after
注册六个事件:五个用于中间名称标签更新,一个用于最终名称更改和弹出。
def spin(self):
def change_name():
index = random.randrange(len(self.name_list))
self.lbl["text"] = self.name_list[index]
self.lbl.grid()
def finish_spinning():
index = random.randrange(len(self.name_list))
self.lbl["text"] = self.name_list[index]
self.lbl.grid()
self.name_list.pop(index)
if self.name_list:
name_changes = 5
for i in range(name_changes):
self.after(100*i, change_name)
self.after(100*name_changes, finish_spinning)
else:
self.lbl["text"] = "No more names"
self.lbl.grid()
(免责声明:这只是一个如何使用after
的简单示例,可能不适合实际使用。特别是,如果按下"旋转&#34,它可能会表现不佳;名称已在旋转时反复按钮。此外,change_name
和finish_spinning
之间的代码重复相当丑陋)
答案 1 :(得分:1)
代码原样可以显示相同的项目两次,因为它每次都会选择一个新的随机数,因此会选择相同数字的时间部分。请注意,在循环之后才会弹出,这意味着每次运行程序时,您将只有一个名称可能更少,也可能不是您想要的名称。如果要保持列表大小相同,可以使用列表的副本,和/或列表中的random.shuffle,并按顺序显示随机列表。此外,您只需要将标签grid()一次,
class Application():
def __init__(self, master):
""" Initialize the frame. """
self.master=master
self.fr=Frame(master)
self.fr.grid()
self.name_list = ["Thorin","Tyler","Jose","Bryson","Joe"]
self.ctr=0
self.create_widget()
def create_widget(self):
self.lbl = Label(self.master width=30)
self.lbl["text"] = "Click to spin"
self.lbl["font"] = ("Arial", 24)
self.lbl.grid()
self.bttn = Button(self.master)
self.bttn["text"]= "Spin"
self.bttn["command"] = self.spin
self.bttn.grid()
def change_label(self):
self.lbl["text"] = self.name_list[self.ctr]
self.ctr += 1
if self.ctr < 5:
self.master.after(1000, self.change_label)
else:
self.ctr=0
def spin(self):
if self.name_list and 0==self.ctr: # not already running
random.shuffle(self.name_list)
self.change_label()
else:
self.lbl["text"] = "No more names"
if __name__ == '__main__':
root = Tk()
root.title("Click Counter")
root.geometry("600x600")
app = Application(root)
root.mainloop()