python - Python Tkinter:如何通过一次删除父小部件来删除所有子小部件?
问题描述
我有一个使用类创建的 HangMan 游戏。我想通过一次删除父窗口来删除所有子小部件。
我的游戏显示了一个难度窗口,在用户选择一个难度并出现主游戏窗口后,该窗口正在被销毁;用户完成游戏后也被销毁,然后出现重播窗口;在用户单击“是”之后,它也会被破坏,并且再次出现一个新的难度弹出实例,整个事情都会发生,直到用户说“不”。
我的代码如下所示:
class HangMan:
font = ('Comic Sans MS', 20, 'bold')
theme = 'Dark'
allWords = []
stages = None
title = None
def __init__(self):
self.tries = None
self.pWord = None
self.word = None
self.used = []
self.char = None
def setup(self, path, stges, title="HangMan"):
with open(path, 'r') as file:
self.allWords = file.read().split()
del file
self.stages = stges
self.title = title
def play(self):
def setTheme(theme, place):
if theme == 'Dark':
b = 'black'
f = 'white'
else:
b = 'white'
f = 'black'
if place == 'askDiff':
self.diffWindow.config(bg = b)
self.diffFrame['bg'] = b
i = [self.diffLabel, self.db1, self.db2, self.db3]
elif place == 'Main':
self.window.config(bg = b)
inputEntry['insertbackground'] = f
for i in [frame1, frame2]:
i['bg'] = b
i = [wordLabel, themeButton, ph, inputEntry, triesLabel, usageLabel, logLabel]
elif place == 'askReplay':
self.replayWindow.config(bg = b)
self.reFrame['bg'] = b
i = [self.reLabel, self.reb1, self.reb2]
for j in i:
j['bg'] = b
j['fg'] = f
def changeTheme():
if self.theme == 'Dark':
self.theme = 'Light'
else:
self.theme = 'Dark'
themeButton['text'] = self.theme
setTheme(self.theme, 'Main')
def replay():
self.replayWindow.destroy()
self.__init__()
self.play()
def askReplay(event):
#Destroying the running game
self.window.destroy()
#Creating the replay Window
self.replayWindow = Tk()
self.replayWindow.title('Re-Experience?')
self.reLabel = Label(master = self.replayWindow, font = self.font, text = "Wanna replay?\nSwear it'll be a different word...")
self.reLabel.pack()
self.reFrame = Frame(master = self.replayWindow)
self.reFrame.pack()
self.reb1 = Button(master = self.reFrame, font = self.font, text = 'Yup', command = replay)
self.reb1.pack(side = 'left')
self.reb2 = Button(master = self.reFrame, font = self.font, text = 'Later.', command = lambda: self.replayWindow.destroy())
self.reb2.pack(side = 'left')
setTheme(self.theme, 'askReplay')
self.replayWindow.mainloop()
def askDiff():
def returnStage(num):
nonlocal x
x = num
self.diffWindow.destroy()
self.diffWindow = Tk()
self.diffWindow.title('Difficulty')
self.diffLabel = Label(master = self.diffWindow, font = self.font, text = 'Choose your level of difficulty: ')
self.diffLabel.pack()
self.diffFrame = Frame(master = self.diffWindow)
self.diffFrame.pack()
x=None
self.db1 = Button(master = self.diffFrame, font = self.font, text = self.stages[0]['stage'], command = lambda: returnStage(0))
self.db1.pack(side = 'left')
self.db2 = Button(master = self.diffFrame, font = self.font, text = self.stages[1]['stage'], command = lambda: returnStage(1))
self.db2.pack(side = 'left')
self.db3 = Button(master = self.diffFrame, font = self.font, text = self.stages[2]['stage'], command = lambda: returnStage(2))
self.db3.pack(side = 'left')
setTheme(self.theme, 'askDiff')
self.diffWindow.mainloop()
return x
def setDiff(i):
from random import choice
wordLenRange = (self.stages[i]['wordLen']['min'], self.stages[i]['wordLen']['max'])
possibleWords = []
for j in self.allWords:
if len(j) in range(wordLenRange[0], wordLenRange[1] + 1):
possibleWords.append(j)
self.word = list(choice(possibleWords).lower())
self.pWord = ''.join(self.word)
self.allWords.remove(self.pWord)
self.tries = self.stages[i]['tries']
def isValid(string):
if len(string)==1 and string.isalpha():
return True
else:
return False
def isUsed(string):
if string in self.used or string in wordLabel['text']:
return True
else:
return False
def isRight(string):
if string in self.word:
return True
else:
return False
def updateLog(x):
if x == 'invalid input':
logLabel['text'] = 'Enter a valid single alphabet'
elif x == 'in word':
logLabel['text'] = 'This letter is revealed'
elif x == 'in used':
logLabel['text'] = 'You have already used this letter'
elif x == 'right':
logLabel['text'] = 'Good Guess...'
elif x == 'wrong':
logLabel['text'] = 'You missed it.'
elif x == 'win':
logLabel['text'] += '\nYou Win...Press any key...'
else:
logLabel['text'] += '\nYou Lost...The word is revealed.\nPress any key...'
def updateWord():
txt = list(wordLabel['text'])
while self.char in self.word:
index = self.word.index(self.char)
txt[index] = self.char
self.word[index] = '*'
txt = ''.join(txt)
wordLabel['text'] = txt
def updateTries():
self.tries -= 1
triesLabel['text'] = '{} Oops left'.format(self.tries)
def updateUsage():
self.used.append(self.char)
usageLabel['text'] = 'Used Letters: {}'.format(', '.join(self.used))
def processGame(event):
self.char = inputEntry.get().strip()
if isValid(self.char):
self.char = self.char.lower()
if isUsed(self.char):
if self.char in wordLabel['text']:
updateLog('in word')
else:
updateLog('in used')
else:
if isRight(self.char):
updateWord()
updateLog('right')
else:
updateTries()
updateUsage()
updateLog('wrong')
else:
updateLog('invalid input')
inputEntry.delete(0, 'end')
if '*' not in wordLabel['text']:
updateLog('win')
inputEntry.bind('<Key>', askReplay)
if self.tries == 0:
updateLog('lost')
wordLabel['text'] = self.pWord
inputEntry.bind('<Key>', askReplay)
## GUI Design
from tkinter import Tk, Label, Entry, Button, Frame
#Diffficulty PopUp
diff = askDiff()
setDiff(diff)
# Main Window
self.window = Tk()
self.window.title(self.title)
# Frames and Buttons
frame1 = Frame(master = self.window)
frame1.pack(fill = 'x')
wordLabel = Label(master = frame1, font = self.font, text = '*'*len(self.word))
wordLabel.pack()
themeButton = Button(master = frame1, font = ("Comic Sans MS", 12), text = self.theme, command = changeTheme)
themeButton.pack(side = 'right')
frame2 = Frame(master = self.window)
frame2.pack()
ph = Label(master = frame2, font = self.font, text = 'Guess a Letter: ')
ph.pack(side = 'left')
inputEntry = Entry(master = frame2, font = self.font, width = 5)
inputEntry.focus_set()
inputEntry.pack(side = 'left')
triesLabel = Label(master = frame2, font = self.font, text = '{} Oops left'.format(self.tries))
triesLabel.pack(side = 'left')
usageLabel = Label(master = self.window, font = self.font, text = 'Used Letters: -')
usageLabel.pack()
logLabel = Label(master = self.window, font = self.font, text = "-:Log:-")
logLabel.pack()
setTheme(self.theme, 'Main')
#Event Handler and MainLoop
self.window.bind('<Return>', processGame)
self.window.mainloop()
path = "words.txt"
stages = ({'stage': 'Cool', 'tries':10, 'wordLen':{'min':3, 'max':5}}, {'stage': 'Engaging', 'tries':6, 'wordLen':{'min':6, 'max':8}}, {'stage': 'Intense', 'tries': 4, 'wordLen': {'min': 9, 'max': 15}})
game1 = HangMan()
game1.setup(path, stages)
game1.play()
在 3 个地方,我正在销毁父窗口 - 在class.play() 函数的 askReplay() 函数中,在class.play() 函数中的 askDiff() 函数的 returnStage() 函数中以及在class.play() 函数的 replay() 函数。我尝试了 self.window.destroy(),但它只是破坏了根窗口,并且它的所有子小部件仍在占用内存(我知道这一点,因为我试图在窗口被破坏后打印其中一个子小部件)。我怎样才能做到这一点?
如果一个痴迷的玩家玩我的游戏一千次,这将是一个问题,在内存中创建了 7000 多个垃圾小部件。
解决方案
一种实现是让顶级对象通过列表跟踪其小部件,然后有一个.close()
看起来像这样的方法:
def close(self):
for widget in self.widgets:
widget.destroy()
self.destroy()
推荐阅读
- reactjs - Chartjs:制作方形图例
- laravel - Laravel 无法读取 vue 文件
- docker - Github Actions Container 未运行
- html - 卡住试图让这两列并排
- javascript - 页面刷新或仅新页面上的“未捕获的类型错误”...?
- javascript - UnhandledPromiseRejectionWarning:TypeError:无法读取未定义的属性“替换”
- java - 为什么 JPanel 大小不对?
- oracle - Oracle 时间范围查询
- python - 蟒蛇插座;我的连接中断了我的数据流
- javascript - 默认排序在 v-data-table Vuetify 中不起作用