Как заставить мои функции работать не одновременно с другим?

Jimmy Fish спросил: 12 мая 2018 в 04:05 в: python

Я пытаюсь создать игру удачи, где вам нужно выбрать 2 шара одного цвета за 3 дверями, и вы 3 попытки, у каждой двери есть 1 мяч, и я заблокирован в точке, где вы открываете одну дверь и Я хочу сделать задержку между моментом, когда дверь покажет шары и момент исчезновения шаров. Я уже пробовал с помощью time.sleep, но он запускает сон в то время с дисплеем. Вот мой код:

import tkinter as tk
import tkinter as tk
from random import shuffle
import time
fenetre = tk.Tk()
fenetre['bg']='black'
fenetre.geometry("1152x768")
color = ["red", "green", "yellow"]
shuffle(color)frameGauche = tk.Frame(width=200, height=600, bg='pink')
frameGauche.grid(row=0, column=0, padx=10, pady=10)frameDroite = tk.Frame(width=700, height=700, bg='grey')
frameDroite.grid(row=0, column=1, padx=10, pady=10)portegauche=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portegauche.grid(row=0, column=0, padx=5, pady=5)portemilieu=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portemilieu.grid(row=0, column=1, padx=5, pady=5)portedroite=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portedroite.grid(row=0, column=2, padx=5, pady=5)def show1(canvas1, bouton2, bouton3):
    canvas1.grid(row=0, column=1)
    bouton2['state']='disabled'
    bouton3['state']='disabled'
    time.sleep(2)
    bouton2['state']='normal'
    bouton3['state']='normal'
    canvas1.grid_remove()
def show2():
    canvas2.grid(row=0, column=2)
    bouton1['state']='disabled'
    bouton3['state']='disabled'
    time.sleep(2)
    bouton1['state']='normal'
    bouton3['state']='normal'
    canvas2.grid_remove()
def show3():
    canvas3.grid(row=0, column=3)
    bouton2['state']='disabled'
    bouton1['state']='disabled'
    time.sleep(2)
    bouton2['state']='normal'
    bouton1['state']='normal'
    canvas3.grid_remove()canvas1=tk.Canvas(portegauche,width=200, height=600, bg='white')
c1 = canvas1.create_oval((60,280), (140,340), width=1, outline="black", 
fill=color[0])
canvas1.grid_forget()canvas2=tk.Canvas(portemilieu,width=200, height=600, bg='white')
c2 = canvas2.create_oval((60,280), (140,340), width=1, outline="black", 
fill=color[1])
canvas2.grid_forget()canvas3=tk.Canvas(portedroite,width=200, height=600, bg='white')
c3 = canvas3.create_oval((60,280), (140,340), width=1, outline="black", 
fill=color[2])
canvas3.grid_forget()def recommencer():
    canvas1.grid_remove()
    canvas2.grid_remove()
    canvas3.grid_remove()
    shuffle(color)
    canvas1.create_oval((60,280), (140,340), width=1, outline="black", fill=color[0])
    canvas2.create_oval((60,280), (140,340), width=1, outline="black", fill=color[1])
    canvas3.create_oval((60,280), (140,340), width=1, outline="black", fill=color[2])
    bouton1['state']='normal'
    bouton2['state']='normal'
    bouton3['state']='normal'boutonR = tk.Button(frameGauche, text='Recommencer',command=recommencer)
boutonR.grid(row=0, column=0, padx=50, pady=50)bouton1=tk.Button(frameDroite, text= 'Ouvrir',command=lambda: show1(canvas1, 
bouton2, bouton3))
bouton1.grid(row=1, column=0)bouton2=tk.Button(frameDroite, text= 'Ouvrir',command=show2)
bouton2.grid(row=1, column=1)bouton3=tk.Button(frameDroite, text= 'Ouvrir',command=show3)
bouton3.grid(row=1, column=2)fenetre.mainloop()

2 ответа

Mike - SMT ответил: 12 мая 2018 в 04:56

Основная проблема заключается в использовании sleep(). Сон в tkinter заставит весь экземпляр замораживаться и не будет работать так, как вы ожидаете. Вместо этого вы можете использовать after().

Также вы дважды импортируете tkinter. Удалите из импорта для tkinter.

Сказали, что нам нужно будет добавить новую функцию и изменить пару строк в ваших функциях.

Я добавил функцию под названием normalize_button(), который принимает 1 аргумент для имени кнопки. Это используется в тандеме с методом after() для обновления кнопок через 2 секунды.

Чтобы ответить на ваш вопрос в комментариях:

Функция normalize_button() вызывается методом after() через 2 секунды. Метод проверяет строку, которая передается ему, и будет обновлять кнопку, основанную на этой строке. Вы можете сделать то же самое с вашим методом show и иметь только один способ обновить все кнопки, если хотите. Это делает вещи немного чистыми и следует за стилем DRY (не повторяйте себя) PEP8.

Взгляните на приведенный ниже код.

import tkinter as tk
from random import shufflefenetre = tk.Tk()
fenetre['bg']='black'
fenetre.geometry("1152x768")
color = ["red", "green", "yellow"]
shuffle(color)frameGauche = tk.Frame(width=200, height=600, bg='pink')
frameGauche.grid(row=0, column=0, padx=10, pady=10)frameDroite = tk.Frame(width=700, height=700, bg='grey')
frameDroite.grid(row=0, column=1, padx=10, pady=10)portegauche=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portegauche.grid(row=0, column=0, padx=5, pady=5)portemilieu=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portemilieu.grid(row=0, column=1, padx=5, pady=5)portedroite=tk.Frame(frameDroite, width=200, 
height=600,bg='white',bd=5,relief='groove')
portedroite.grid(row=0, column=2, padx=5, pady=5)def normalize_button(btn_name):
    print(btn_name)
    if btn_name == "b1":
        bouton1['state']='normal'
    if btn_name == "b2":
        bouton2['state']='normal'
    if btn_name == "b3":
        bouton3['state']='normal'def show1():
    canvas1.grid(row=0, column=1)
    bouton2['state']='disabled'
    bouton3['state']='disabled'
    fenetre.after(2000, normalize_button, "b2")
    fenetre.after(2000, normalize_button, "b3")
    fenetre.after(2000, canvas1.grid_forget)def show2():
    canvas2.grid(row=0, column=2)
    bouton1['state']='disabled'
    bouton3['state']='disabled'
    fenetre.after(2000, normalize_button, "b1")
    fenetre.after(2000, normalize_button, "b3")
    fenetre.after(2000, canvas2.grid_forget)def show3():
    canvas3.grid(row=0, column=3)
    bouton2['state']='disabled'
    bouton1['state']='disabled'
    fenetre.after(2000, normalize_button, "b2")
    fenetre.after(2000, normalize_button, "b1")
    fenetre.after(2000, canvas3.grid_forget)canvas1 = tk.Canvas(portegauche,width=200, height=600, bg='white')
c1 = canvas1.create_oval((60,280), (140,340), width=1, outline="black", fill=color[0])
canvas1.grid_forget()canvas2 = tk.Canvas(portemilieu,width=200, height=600, bg='white')
c2 = canvas2.create_oval((60,280), (140,340), width=1, outline="black", fill=color[1])
canvas2.grid_forget()canvas3 = tk.Canvas(portedroite,width=200, height=600, bg='white')
c3 = canvas3.create_oval((60,280), (140,340), width=1, outline="black", fill=color[2])
canvas3.grid_forget()def recommencer():
    canvas1.grid_remove()
    canvas2.grid_remove()
    canvas3.grid_remove()
    shuffle(color)
    canvas1.create_oval((60,280), (140,340), width=1, outline="black", fill=color[0])
    canvas2.create_oval((60,280), (140,340), width=1, outline="black", fill=color[1])
    canvas3.create_oval((60,280), (140,340), width=1, outline="black", fill=color[2])
    bouton1['state']='normal'
    bouton2['state']='normal'
    bouton3['state']='normal'boutonR = tk.Button(frameGauche, text='Recommencer',command=recommencer)
boutonR.grid(row=0, column=0, padx=50, pady=50)bouton1=tk.Button(frameDroite, text= 'Ouvrir',command=show1)
bouton1.grid(row=1, column=0)bouton2=tk.Button(frameDroite, text= 'Ouvrir',command=show2)
bouton2.grid(row=1, column=1)bouton3=tk.Button(frameDroite, text= 'Ouvrir',command=show3)
bouton3.grid(row=1, column=2)fenetre.mainloop()
Jimmy Fish ответил: 12 мая 2018 в 04:45
Спасибо, он работает отлично, но можете ли вы объяснить мне часть "normalize_button", пожалуйста?
Mike - SMT ответил: 13 мая 2018 в 01:25
@JimmyFish Пожалуйста, найдите время, чтобы выбрать галочку рядом с ответом, который лучше всего решил вашу проблему.
Mike - SMT ответил: 12 мая 2018 в 04:59
@JimmyFish Я добавил несколько подробностей к объяснению функции normalize.
d parolin ответил: 12 мая 2018 в 05:03

Как уже упоминалось, sleep () заморозит выполнение и никогда не будет хорошей идеей в программировании графического интерфейса.

Чтобы получить эту работу, раскройте ваши функции showx в show and hide, вот пример show1. Здесь мы используем .after для вызова лямбда-функции (что упрощает передачу аргументов), первый аргумент .after - это время в миллисекундах

def show1(canvas1, bouton2, bouton3):
    canvas1.grid(row=0, column=1)
    bouton2['state']='disabled'
    bouton3['state']='disabled'
    # OLD fenetre.after(2000, lambda: hide1(canvas1, bouton2, bouton3))
    # New thanks to Mike-SMT
    fenetre.after(2000, hide1, canvas1, bouton2, bouton3)def hide1(canvas1, bouton2, bouton3):
    bouton2['state']='normal'
    bouton3['state']='normal'
    canvas1.grid_remove()

Здесь ответ I получил некоторые данные из Tkinter после

[EDIT] Вы можете упростить это, создав одну функцию show и one hide и передав объекты canvas / button как аргументы, есть больше, чтобы рефакторировать, чтобы сделать это проще в долгосрочной перспективе, но чтобы начать использовать использование .after () должно быть тем, что вам нужно сейчас.

Вы можете удалить двойную import в верхней части файла

import tkinter as tk
Mike - SMT ответил: 12 мая 2018 в 04:39
ваша функция скрытия неполна.
d parolin ответил: 12 мая 2018 в 04:47
Привет @ Mike-SMT, я не уверен, в каком смысле, пожалуйста, помогите мне улучшить мой ответ, учитывая исходный код, который, кажется, делает то, что необходимо, я тем не менее даю его вам, чтобы ваш ответ был намного более сложным и лучшим ,
Mike - SMT ответил: 12 мая 2018 в 04:58
Неважно. Я вижу, что вы делаете сейчас. Однако вам здесь не нужна лямбда. Вместо этого вы можете просто сделать это. fenetre.after(2000, hide1, canvas1, bouton2, bouton3).
Mike - SMT ответил: 12 мая 2018 в 05:13
Я согласен, что это можно уменьшить, и на самом деле это можно свести только к одной функции, которая принимает несколько аргументов, а затем выполняет команды show и hide на основе этих аргументов.