Python Tkinter:如何通过一次删除父小部件来删除所有子小部件?

时间:2021-05-19 04:37:29

标签: 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() 函数中,在 askDiff() 的 returnStage() 函数中) 函数在 class.play() 函数中 & 在 replay() 函数中的 class.play() 函数。我尝试了 self.window.destroy(),但它只是破坏了根窗口,并且它的所有子小部件仍在占用内存(我知道这一点是因为我尝试在窗户被毁了)。我该怎么做?

<块引用>

如果一个痴迷玩家玩我的游戏一段时间,这将是一个问题 千次,在内存中创建了 7000 多个垃圾小部件。

1 个答案:

答案 0 :(得分:0)

一种实现是让顶级对象通过列表跟踪其小部件,然后使用 .close() 方法,如下所示:

def close(self):
    for widget in self.widgets:
        widget.destroy()
    self.destroy()