[Tutor] MineSweeper

On Sat, 25 Jun 2005, Alberto Troiano wrote:
> My doubt is that I want to be able to allow the user to put how many
> bombs they want and how any spaces (how big is the board) so I will have
> to create buttons dinamically, but in doing this the reference to the
> buttons will be lost How do I know which button did I click when all
> buttons have the same name? Does this make sense?
Hi Alberto,
[The following is a tutorial about using functions as values; it's a bit
long, so if you have any questions at all, ask!]
In Tkinter, each button can be associated with a "command" that gets
called when we press a button.
### Example One ###
from Tkinter import *
def sayRefrain():
print "... for the rain, it raineth every day"
root = Tk()
button = Button(root, text="hey ho the wind and the rain",
command=sayRefrain)
button.pack()
root.mainloop()
###################
Note that when we say that our 'button' should use 'sayRefrain', not
'sayRefrain()'. We're using a function as a value that the button will
remember. Just as the button knows what text it should show, it also
knows which function it should call when we press it.
So that's a quick and dirty example of a window with one button. But
let's say that we have two buttons, now. How can we distinguish one
button from another button? We can do this by attaching two separate
commands to the individual buttons:
### Example Two ###
from Tkinter import *
def sayRefrain1():
print "... for the rain, it raineth every day"
def sayRefrain2():
print "... and we'll strive to please you every day"
root = Tk()
button1 = Button(root, text="hey ho the wind and the rain",
command=sayRefrain1)
button2 = Button(root, text="hey ho the wind and the rain",
command=sayRefrain2)
button1.pack()
button2.pack()
root.mainloop()
##################
Ok, now we have two buttons in our example. We can distinguish between
buttons by associating different commands to them: they have the same
text, but they do different things.
By the way, just to make sure this point is clear: we're just passing the
function values of sayRefrain1 and sayRefrain2 instead of calling them
directly. If we just ask Python what the value of sayRefrain1 is:
#######
>>> sayRefrain1
<function sayRefrain1 at 0x403d233c>
#######
it's just a value, just as:
#######
>>> "hey ho the wind and the rain"
'hey ho the wind and the rain'
#######
is just a value, just like the familiar numbers and strings that you've
been using before.
Function values are a little special in one regard: they do things if we
put parens after them, as you know:
#######
>>> sayRefrain2()
... and we'll strive to please you every day
#######
but being able to call a function value is pretty much all that
distinguish a function value from other Python values.
Ok, let's get back to our examples. We went from an example with one
button, to an example with two. I think you can guess where we're about
to go. *grin*
Now let's say that we have a few more buttons that we'd like to use. We
know what we'd like each button should say:
#######
buttonMessages = ["When that I was and a little tiny boy,",
"But when I came to main's estate,",
"But when I came alas to wive,",
"But when I came unto my beds,"]
#######
But let's wait for a moment before plunging forward. If we go along the
path that our other examples have been going through, it might be a little
tedious to define a separate sayRefrainX for every message in our list.
But we can do a little better. Let's try something new:
######
def makeRefrainPrinter(message):
"""Given a message, returns a new function that'll say the message."""
def sayRefrain():
print message
return sayRefrain
######
What does makeRefrainPrinter() do? Let's try playing with it:
######
>>> makeRefrainPrinter("as I am an honest puck")
<function sayRefrain at 0x404bd534>
######
If we call makeRefrainPrinter(), it gives us back a function value. Let's
see what happens if we call that function value:
######
>>> someFunctionValue = makeRefrainPrinter("as I am an honest puck")
>>> someFunctionValue()
as I am an honest puck
######
makeRefrainPrinter() makes up function values, on the fly, so that we can
make any kind of sayRefrainX function value with relative ease.
If we have this makeRefrainPrinter function, now our example code can look
like this:
### Example Three ###
from Tkinter import *
buttonMessages = ["When that I was and a little tiny boy,",
"But when I came to main's estate,",
"But when I came alas to wive,",
"But when I came unto my beds,"]
def makeRefrainPrinter(message):
"""Given a message, returns a new function that'll say the message."""
def sayRefrain():
print message
return sayRefrain
root = Tk()
for msg in buttonMessages:
someFunctionValue = makeRefrainPrinter(msg)
button = Button(root,
text="hey, ho, the wind and the rain",
command=someFunctionValue)
button.pack()
root.mainloop()
#####################
Does this make sense so far? This is still a little bit of a way from
getting a Minesweeper GUI, but it's surprisingly close to the minimal
concepts you can use to make a Minesweeper-like program.
I hope this helps!