Reputation: 105
I'm trying to make a memory game using python 3 and tkinter. I'm new to python so this is kind of hard. My problem is that I'm trying to call the method "show_word" in the class "Cell" from the class "Memory" but I don't know exactly how to do it.
the memory game:
def create_widgets(self):
""" Create widgets to display the Memory game """
# buttons to show the words
column = 0
row = 1
the_pairs = self.readShuffle()
for index in range(36):
Button(self,
text = "hidden",
width = "7",
height = "2",
relief = GROOVE,
command = WHAT SHOULD I WRITE HERE???
).grid(row = row, column = column, padx = 1, pady = 1)
column += 1
if column == 6:
column = 0
row += 1
Upvotes: 1
Views: 4662
Reputation: 151177
You've run into a subtle scoping problem. When you create a function that refers to variables from a containing scope, the value of those variables isn't fixed at the time of function definition, but at the time of function execution. In other words, when you do this:
command = lambda: print(index)
You're telling Python to print whatever the value of index
is when the function is called. By the time a user pushes one of those buttons, causing the function to be called, the value of index
is 35.
To fix the value at the time of function definition, you have to use default values, like so:
command = lambda x=index: print(x)
I'm sure you can figure out the corresponding show_word
fix, but just in case:
command = lambda x=index: Cell.show_word(the_pairs[x])
Upvotes: 3
Reputation: 61643
I need an answer for my comment before I can help you directly, but in the mean time, here are some hints for writing the code more elegantly:
from tkinter import *
# `tkinter` is meant to be used that way, but `random` isn't really - the names
# can be confusing without an explicit module reference. So instead:
import random
class Cell:
def __init__(self, word, hidden):
self.word = word
self.hidden = hidden
def show_word(self):
""" Shows the word behind the cell """
self.hidden = not self.hidden
# no need to take this logic apart into cases
self.button.set(str(self))
def __str__(self):
""" Displays or hides the word """
# A simpler form of conditional; also, don't compare things
# explicitly to True or False
return "---" if self.hidden else self.word
class Memory(Frame):
""" GUI application that creates a Memory game """
def __init__(self, master):
super(Memory, self).__init__(master)
self.grid()
self.create_widgets()
self.tries = 0
def readShuffle(self):
""" Creates and organizes (shuffles) the pairs in a list """
# The modern idiom for handling files and ensuring they are closed
with open("memo.txt","r") as words_file:
# The file can be iterated over directly, and is treated as
# a list of lines. Since we just want all the rstrip()ped
# lines, we can do the file processing all at once with a list
# comprehension.
# Let's also grab 18 random words while we're at it. We don't
# really want to *shuffle* the words, but instead *sample* them -
# we don't care about the words that we didn't select.
words = random.sample(
[line.rstrip('\n') for line in words_file], 18
)
# Instead of making 18 pairs of cells, we can make 18 cells and then
# pair them up. The set of 18 cells can also be made easily with a
# list comprehension. Notice how we get to iterate directly now,
# instead of messing around with indices into lists.
the_pairs = [Cell(word, True) for word in words] * 2
shuffle(the_pairs)
return the_pairs
def create_widgets(self):
""" Creates widgets to display the Memory game """
# instruction text
Label(self,
text = "- The Memory Game -",
font = ("Helvetica", 12, "bold"),
).grid(row = 0, column = 0, columnspan = 7)
# buttons to show the words
the_pairs = self.readShuffle()
self.buttons = []
# Again, we can iterate in a more Pythonic way.
for i, pair in enumerate(the_pairs):
# Instead of having extra counters to work out the row and column,
# we can simply do math on the index value.
column, row = i % 6, i // 6
temp = StringVar()
temp.set(str(pair))
# Instead of adding the button to a list and then reaching into the
# list to configure it, get everything set up first.
button = Button(self,
textvariable = temp,
width = "7",
height = "2",
relief = GROOVE,
command = lambda: print(index)
))
button.grid(row = row, column = column, padx = 1, pady = 1)
buttons.append(button)
pair.button = temp
# total tries
self.label = Label(self) # Don't abbreviate!
Label(self,
text = "Total tries: 0",
font = ("Helvetica", 11, "italic")
).grid(row = 7, columnspan = 7, pady = 5)
# ...
Upvotes: 0