I'm working on an RPG-style game using the tkinter
module in Python and I have a character creation screen with randomly-generated stats. The 6 stats are generated by using the random
module.
The problem that I'm having is when I clear the screen to repack the numbers into the frame dedicated to them it works the first time, the numbers clear and a new set of numbers appears, however the second and third time (and so on) I press the button it just generates a new set of numbers inside the frame whilst the previous numbers remain, duplicating the amount of numbers.
I've tried running various different methods seen from this site such as .pack_forget
and deleting from the screen, I even set the variables used to store the Labels to None
but nothing I try seems to work.
I'll post the code below, the program is several hundred lines so forgive me if I missed anything related to the problem.
def CharManage2Option3Command(StrengthValue, DexterityValue, ConstitutionValue, WisdomValue, IntelligenceValue, CharismaValue, statframebottomleftright, statframebottomrightright):
StrengthValue.destroy()
DexterityValue.destroy()
ConstitutionValue.destroy()
WisdomValue.destroy()
IntelligenceValue.destroy()
CharismaValue.destroy()
StrNumber = randrange(3, 18)
DexNumber = randrange(3, 18)
ConNumber = randrange(3, 18)
WisNumber = randrange(3, 18)
IntNumber = randrange(3, 18)
ChaNumber = randrange(3, 18)
StrengthValue = Label(statframebottomleftright, text=StrNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
DexterityValue = Label(statframebottomleftright, text=DexNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
ConstitutionValue = Label(statframebottomleftright, text=ConNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
WisdomValue = Label(statframebottomrightright, text=WisNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
IntelligenceValue = Label(statframebottomrightright, text=IntNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
CharismaValue = Label(statframebottomrightright, text=ChaNumber, fg=DefaultColour, bg=WhiteBackgroundColour, font=DefaultFont)
StrengthValue.pack(fill="both", expand=True)
DexterityValue.pack(fill="both", expand=True)
ConstitutionValue.pack(fill="both", expand=True)
WisdomValue.pack(fill="both", expand=True)
IntelligenceValue.pack(fill="both", expand=True)
CharismaValue.pack(fill="both", expand=True)
The problem you're having is that the assignment e.g. StrengthValue = Label(...)
inside the function does not affect the object that was passed in, it just assigns a new object to that name inside the function. Therefore the first time you call the function the original objects are destroy
ed as expected, but you don't retain a reference to the new objects (as they aren't return
ed from the function) so subsequent calls appear to behave incorrectly.
One solution is to pass mutable arguments, e.g. lists containing the relevant objects:
def CharManage2Option3Command(label_lists, stat_frames):
for label_list, stat_frame in zip(label_lists, stat_frames):
for index, label in enumerate(label_list):
number = randrange(3, 18)
label.destroy()
label_list[index] = Label(stat_frame, text=number, ...)
label_list[index].pack(...)
(see the docs on zip
and enumerate
.). This would then be called e.g.:
CharManage2Option3Command([[StrengthValue, DexterityValue, ConstitutionValue],
[WisdomValue, IntelligenceValue, CharismaValue]],
[statframebottomleftright, statframebottomrightright]):
Note this has the handy side-effect of reducing duplication in your function. You could also keep the Label
instances in a list from the start, rather than keeping a separate reference to each one.
Other solutions include explicitly return
ing the new labels from the function and assigning back to the original names:
def CharManage2Option3Command(StrengthValue, DexterityValue, ConstitutionValue, WisdomValue, IntelligenceValue, CharismaValue, statframebottomleftright, statframebottomrightright):
...
return StrengthValue, DexterityValue, ConstitutionValue, WisdomValue, IntelligenceValue, CharismaValue
then calling it:
StrengthValue, DexterityValue, ConstitutionValue, WisdomValue, IntelligenceValue, CharismaValue = CharManage2Option3Command(StrengthValue, DexterityValue, ConstitutionValue, WisdomValue, IntelligenceValue, CharismaValue, statframebottomleftright, statframebottomrightright)
(which is obviously pretty awkward), using an IntVar
as the textvariable
for the Label
and updating that or doing something class-based, such that you access e.g. self.StrengthValue
everywhere.
I also suggest you have a look at the style guide, which provides conventions for things like function and variable names.