Python Forum
How can I add reset button?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How can I add reset button?
#1
I need to add a reset button/option. Can someone help me?

import random
import time
from tkinter import Tk, Button, DISABLED, messagebox


def close_window(self):
    root.destroy()

def show_symbol(x, y):
    global first
    global previousX, previousY
    global moves
    global pairs
    buttons[x, y]['text'] = button_symbols[x, y]
    buttons[x, y].update_idletasks()
    if first:
        previousX = x
        previousY = y
        first = False
        moves = moves + 1
    elif previousX != x or previousY != y:
        if buttons[previousX, previousY]['text'] != buttons[x, y]['text']:
            time.sleep(0.5)
            buttons[previousX, previousY]['text'] = ''
            buttons[x, y]['text'] = ''
        else:
            buttons[previousX, previousY]['command'] = DISABLED
            buttons[x, y]['command'] = DISABLED
            pairs = pairs + 1
            if pairs == len(buttons) / 2:
                messagebox.showinfo('Matching', 'Number of moves: ' + str(moves))
        first = True





root = Tk()
root.title('Igra Memorije')
root.resizable(width=False, height=False)

buttons = {}
first = True
previousX = 0
previousY = 0
moves = 0
pairs = 0


button_symbols = {}
symbols = [u'\u2702', u'\u2702', u'\u2705', u'\u2705', u'\u2708', u'\u2708',
           u'\u2709', u'\u2709', u'\u270A', u'\u270A', u'\u270B', u'\u270B',
           u'\u270C', u'\u270C', u'\u270F', u'\u270F', u'\u2712', u'\u2712',
           u'\u2714', u'\u2714', u'\u2716', u'\u2716', u'\u2728', u'\u2728',
          ]

random.shuffle(symbols)


for x in range(6):
    for y in range(4):
        button = Button(command=lambda x=x, y=y: show_symbol(x, y), width=5, height=3, border=2)
        button.grid(column=x, row=y,padx=15,pady=20)
        buttons[x, y] = button
        button_symbols[x, y] = symbols.pop()


root.mainloop()
Gribouillis write Jun-06-2022, 07:14 PM:
Please post all code, output and errors (in it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.

Attached Files

.py   non.py (Size: 1.81 KB / Downloads: 127)
Reply
#2
Migrate your code to a class, it eliminates the use of globals.
One way to clear a number of buttons is assign them to a frame. Them you can
pack_forget() and clear it all. Make more functions, I modified your script into 2 functions,
the other part could be all in your class init function.
here's one way to reset your game
import random
import time
from tkinter import Tk, Button, DISABLED, messagebox, Frame
from functools import partial
 
 
def close_window():
    root.destroy()
 
def show_symbol(x, y):
    global first
    global previousX, previousY
    global moves
    global pairs
    
    buttons[x, y]['text'] = button_symbols[x, y]
    buttons[x, y].update_idletasks()
    if first:
        previousX = x
        previousY = y
        first = False
        moves = moves + 1
    elif previousX != x or previousY != y:
        if buttons[previousX, previousY]['text'] != buttons[x, y]['text']:
            time.sleep(0.5)
            buttons[previousX, previousY]['text'] = ''
            buttons[x, y]['text'] = ''
        else:
            buttons[previousX, previousY]['command'] = DISABLED
            buttons[x, y]['command'] = DISABLED
            pairs = pairs + 1
            if pairs == len(buttons) / 2:
                messagebox.showinfo('Matching', 'Number of moves: ' + str(moves))
                frame.pack_forget()
                frame.pack(expand='yes',fill='both')
                make_board()
        first = True
 
 
def make_board():
    global button_symbols
    my_font= ('arial',30,'bold')
    button_symbols = {}
    symbols = [u'\u2702', u'\u2702', u'\u2705', u'\u2705', u'\u2708', u'\u2708',
               u'\u2709', u'\u2709', u'\u270A', u'\u270A', u'\u270B', u'\u270B',
               u'\u270C', u'\u270C', u'\u270F', u'\u270F', u'\u2712', u'\u2712',
               u'\u2714', u'\u2714', u'\u2716', u'\u2716', u'\u2728', u'\u2728',
              ]
 
    random.shuffle(symbols)
 
 
    for x in range(6):
        for y in range(4):
            button = Button(frame,command=partial(show_symbol, x, y),
                            width=5, height=3, border=2,font= my_font)
            button.grid(column=x, row=y,padx=15,pady=20)
            buttons[x, y] = button
            button_symbols[x, y] = symbols.pop()
 
 
root = Tk()
root.title('Igra Memorije')
root.resizable(width=False, height=False)

frame= Frame(root)
frame.pack(expand='yes',fill='both')
 
buttons = {}
first = True
previousX = 0
previousY = 0
moves = 0
pairs = 0

make_board()

 

 
 
root.mainloop()
Reply
#3
You want to set the button "state" = DISABLED, not the button "command". The button command should always be "show_symbol".

There is nothing special about the x,y of the buttons. I would make buttons a list instead of a dictionary. This removes some processing because you can use the initial symbols list instead of copying it to a dictionary.

You do not want to unpack the buttons, you just want to set the text to blank and the state to NORMAL.

When making the symbols list you don't have to type each symbol twice. Let Python do that for you. Less typing and less chance for error.

This is the program written as a class.
import random
import time
import tkinter as tk
from tkinter.messagebox import showinfo
 
symbols = [
    "\u2702",
    "\u2705",
    "\u2708",
    "\u2709",
    "\u270A",
    "\u270B",
    "\u270C",
    "\u270F",
    "\u2712",
    "\u2714",
    "\u2716",
    "\u2728",
]
 
font = ("Times", 24)
 
class MatchingGame(tk.Tk):
    """A tiles matching game.
    Click on a tile to display it's picture.  Click on a second tile.
    If the pictures match, try to find the next match.  If the tiles
    do not match the pictures are hidden.  The game is over when you
    match all the tiles.
    """
    def __init__(self, symbols, columns):
        super().__init__()
        self.title("Matching Game")
        self.symbols = symbols * 2
        self.tiles = [tk.Button(self, width=3, border=2, font=font) for _ in self.symbols]
        for index, tile in enumerate(self.tiles):
            tile.configure(command=lambda arg=tile: self.show_symbol(arg))
            tile.grid(row=index // columns, column=index % columns)
        self.reset()
 
    def reset(self):
        """Reset the board to start a new game"""
        random.shuffle(self.symbols)
        for tile, symbol in zip(self.tiles, self.symbols):
            tile.configure(text=" ", state=tk.NORMAL)
            tile.symbol = symbol
        self.prev = None
        self.moves = 0
        self.remaining = len(self.tiles)
 
    def show_symbol(self, tile):
        """Uncover a the tile.  If two tiles exposed check for match."""
        tile.configure(text=tile.symbol, state=tk.DISABLED)
        self.update_idletasks()
 
        if self.prev is None:
            self.prev = tile
            self.moves += 1
        else:
            # Second tile.  Check for match
            if self.prev.symbol == tile.symbol:
                self.remaining -= 2
            else:
                # Tiles do not match.  Wait a moment and hide
                time.sleep(1)
                tile.configure(text=" ", state=tk.NORMAL)
                self.prev.configure(text=" ", state=tk.NORMAL)

            self.prev = None
            if self.remaining == 0:
                showinfo("Game Over", f"Number of moves: {self.moves}")
                self.reset()
 
MatchingGame(symbols, 6).mainloop()
One class works fine for a simple game like this, but in a more complicated game you would make multiple classes so that no one class is responsible for doing too many things. Let's say I want to separate the code of how the tiles work into it's own class so we have a class that knows how to play the matching game, and another class that knows how to show/hide the tile symbols.
import random
import time
import tkinter as tk
from tkinter.messagebox import showinfo
 
symbols = [
    "\u2702",
    "\u2705",
    "\u2708",
    "\u2709",
    "\u270A",
    "\u270B",
    "\u270C",
    "\u270F",
    "\u2712",
    "\u2714",
    "\u2716",
    "\u2728",
]

class Tile(tk.Button):
    """A special button for the matching game"""
    font = ("Times", 24)

    def __init__(self, parent, symbol, callback):
        super().__init__(parent, width=3, border=2, font=self.font, command=self.click)
        self.symbol = symbol
        self.callback = callback

    def __eq__(self, other):
        """Tiles are equal if their symbols match"""
        return self.symbol == other.symbol

    def click(self):
        """Callback when button is clicked.  Show symbol and disable clicking"""
        self.configure(text=self.symbol, state=tk.DISABLED)
        self.callback(self)

    def clear(self):
        """Hide symbol ane enable clicking"""
        self.configure(text=" ", state=tk.NORMAL)

class MatchingGame(tk.Tk):
    """A tiles matching game.
    Click on a tile to display it's picture.  When two tiles are uncovered,
    check for a match.  If tiles match do not match, cover the tiles and try
    again.  The game is over when all tiles are uncovered.
    """
    def __init__(self, symbols, columns):
        super().__init__()
        self.title("Matching Game")
        self.tiles = [Tile(self, symbol, self.check_match) for symbol in symbols * 2]
        self.columns = columns
        self.reset()
 
    def reset(self):
        """Reset the board to start a new game"""
        random.shuffle(self.tiles)
        for index, tile in enumerate(self.tiles):
            tile.clear()
            tile.grid(row=index // self.columns, column=index % self.columns)
        self.prev = None
        self.moves = 0
        self.remaining = len(self.tiles)
 
    def check_match(self, tile):
        """Check if uncovered tiles match."""
        if self.prev is None:
            self.prev = tile
            self.moves += 1
        else:
            # Two tiles are uncovered.  Check for match
            if self.prev == tile:
                self.remaining -= 2
            else:
                # Tiles do not match.  Wait a moment and hide
                self.update_idletasks()
                time.sleep(1)
                tile.clear()
                self.prev.clear()

            self.prev = None
            if self.remaining == 0:
                showinfo("Game Over", f"Number of moves: {self.moves}")
                self.reset()
 
MatchingGame(symbols, 6).mainloop()
The number of lines of code increased, but I think the matching game code is a little easier to understand now that it is focused on game play and the Tile class takes on the responsibility of flipping tiles.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Reset Button did not work ardvci 2 2,723 Mar-02-2020, 07:59 PM
Last Post: ardvci
  [PySimpleGui] How to alter mouse click button of a standard submit button? skyerosebud 3 4,951 Jul-21-2019, 06:02 PM
Last Post: FullOfHelp
  [Tkinter] Reset Button CPD3408 3 19,768 Jan-25-2019, 11:55 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020