-1

I'm making a memory game in Python Tkinter. After a card is flipped, I'm using the after method to make the reset function in my code delay for one second before the card is flipped back. It makes the function just wait forever. Can someone please explain why it's doing that? My code:

from tkinter import *
from random import choice

screen = Tk()
screen.title("Disney Princesses Memory Game")
width = screen.winfo_screenwidth()
height = screen.winfo_screenheight()
screen.geometry("%dx%d" % (width, height))
screen.configure(bg="#e0bce5")

title = Label(screen, text="Memory Game", font=("David", 50, "underline", "bold"), bg="#e0bce5")
title.place(x=400, y=20)

images_list = [
    PhotoImage(file="images/aurora.png"),
    PhotoImage(file="images/belle.png"),
    PhotoImage(file="images/cinderella.png"),
    PhotoImage(file="images/jasmine.png"),
    PhotoImage(file="images/mulan.png"),
    PhotoImage(file="images/rapunzel.png"),
    PhotoImage(file="images/snow white.png"),
    PhotoImage(file="images/tiana.png")
]

buttons_list = []
chosen_images = []
flipped = []
num_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
card = PhotoImage(file="images/card.png")
count = 0
no_press = False


def choose_images():
    for num in range(16):
        chosen_image = choice(images_list)
        num_list[num] += 1

        if num_list[num] > 2:
            num += 1
            continue
        else:
            chosen_images.append(chosen_image)


def replace_card(c, d):
    global flipped, count, no_press

    if no_press is True:
        return

    count += 1

    if count > 2:
        count = 0
        no_press = True
        sleep_secs()
    else:
        buttons_list[c][d].configure(image=chosen_images[d])
        flipped.append(buttons_list[c][d])


def reset():
    global card, no_press
    for element in flipped:
        element.configure(image=card)


def sleep_secs():
    global no_press
    screen.after(1000, reset)
    no_press = False


choose_images()
x = 100
y = 250
for i in range(2):
    buttons_list.append([])
    for j in range(8):
        a = Button(screen, image=card, command=lambda i=i, j=j: replace_card(i, j))
        x += 100
        a.place(x=x, y=y)
        buttons_list[i].append(a)
    y += 100
    x = 100

screen.mainloop()

The weird thing about the after method for me is that it worked when I implemented it in a smaller program:

from tkinter import *

screen = Tk()
screen.geometry("600x800+30+10")
hi = Label(screen)
hi.place(x=20, y=20)


def reset():
    hi.configure(text="hi", font=("Ariel", 30))


def sleep_secs():
    screen.after(2000, reset)
    print(2)


sleep_secs()
screen.mainloop()

What's the deal with that?

Roni
  • 597
  • 5
  • 21
  • Can you explain what just making part of a program sleep means? – Scott Hunter Jul 15 '21 at 12:33
  • tkinter has its own async "call this other function in N seconds" mechanism. You should use that rather than any of the generic, non-tkinter-aware answers thus far given. (This kind of situation is part of why we have the close-as-duplicate mechanism, to guide folks to somewhere the set of available answers is already comprehensive, and where those answers have already had the benefit of comment feedback, voting, etc) – Charles Duffy Jul 15 '21 at 12:36
  • @Scott Hunter, it means delaying the program by a couple of seconds – Roni Jul 15 '21 at 12:36
  • @Charles Duffy, can you make an answer and show me how to implement it? – Roni Jul 15 '21 at 17:07
  • 1
    @Roni Look at [this](https://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method). I think @ CharlesDuffy is talking about the `.after` method. But be careful: the first argument is in milliseconds. – TheLizzard Jul 15 '21 at 17:10
  • 1
    @Roni Try [this](https://pastebin.com/8ep6mFCP) – TheLizzard Jul 15 '21 at 17:26
  • It helped in one way, but then introduced me to a new bug. The program waits for more than the amount of seconds that I put in "sleep" – Roni Jul 15 '21 at 17:49
  • @Roni, even `time.sleep` is allowed to sleep for longer than you ask it to. Only real-time operating systems guarantee to get back exactly when expected. OTOH, if it's a _substantial_, reproducible difference on an unloaded system, that might be worth a Stack Overflow question with a reproducer showing how to demonstrate the problem. – Charles Duffy Jul 15 '21 at 18:18
  • oh, so what should I do? – Roni Jul 15 '21 at 18:20
  • 1
    Really, I'd ask a question with a [mre] that measures and quantifies how the `.after` approach in tkinter is delaying much longer than it should. It's a new and different question, and a worthy one. – Charles Duffy Jul 15 '21 at 18:21

1 Answers1

1

Try with async function:

from random import randint
import time
import asyncio


async def randn():
   await asyncio.sleep(3)
    return randint(1, 10)
Mhamed Bendenia
  • 180
  • 1
  • 8
  • Hi, thanks for answering. I’m not very familiar with the async function. Can you explain to me what’s the use of “return randint(1, 10)”? Why do we need to return a random integer between 1 and 10? – Roni Jul 15 '21 at 12:38
  • It's just an example, replace it with the core of your function. – Mhamed Bendenia Jul 15 '21 at 12:40
  • I don't think this would work...Could you be more specific? – jizhihaoSAMA Jul 15 '21 at 12:52
  • @MhamedBendenia Please add an explanation to how the code in your answer works. It isn't going to help OP if they don't understand it. If OP uses your code but doesn't understand it, it can make their program undebuggable. – TheLizzard Jul 15 '21 at 13:28
  • It doesn't work. The error: C:\Users\Meirom\PycharmProjects\memorygame\main.py:62: RuntimeWarning: coroutine 'sleep_secs' was never awaited sleep_secs() RuntimeWarning: Enable tracemalloc to get the object allocation traceback – Roni Jul 15 '21 at 17:18
  • can you please show me how to do it with my code? – Roni Jul 15 '21 at 17:18
  • 1
    @Roni I have no idea how `asyncio` works but does it use another thread/process to run the code? If it does, then you should call `tkinter` functions from `asyncio` because `tkinter` isn't threadsafe and some times it can just crash. – TheLizzard Jul 15 '21 at 17:28
  • I didn't use threads. I'm pretty much a Python beginner/almost intermediate, so I'm not familiar with threads yet – Roni Jul 15 '21 at 17:38
  • My bad. I wanted to tag @MhamedBendenia on my last comment. – TheLizzard Jul 15 '21 at 17:43
  • Sorry for the fuzzy answer @TheLizzard, here you can find all what you need regarding asynchronous functions : https://www.aeracode.org/2018/02/19/python-async-simplified/ – Mhamed Bendenia Jul 18 '21 at 13:30