在Tkinter画布中的while循环中更改图像

5
我的全部代码在这里。
我正在使用tkinter的画布,试图创建一个小游戏,让人们练习学习谱号上的音符。
一开始会显示一个随机的音符,用户必须选择正确的音符。但是如果学生答对了,我无法编辑画布以显示新音符的图片。请问有谁能解释一下我的代码存在什么问题吗?
while True:
    randomNote = randint(0, 11)
    path = noteFiles[randomNote]
    correctNote = notes[randomNote]
    img = Image.open(path)
    tk_img = ImageTk.PhotoImage(img)
    canvas.create_image(505, 200, image=tk_img)

    root.mainloop()
    if correctNote == noteChosen:
        ''' User got last note right '''
        canvas.delete("all")

任何在调用mainloop()之后的代码都不会在程序退出之前执行,如果有的话。您需要安排您的代码在循环期间执行 - 例如按键绑定、按钮上的command=选项或其他各种方式。您提供的信息远远不足以得到一个具体的答案,因为我们不知道您的程序应该如何工作。 - jasonharper
我对这个东西比较新,所以不太知道该说什么,但如果需要的话,我可以提供剩下的代码。它并不长。 - user3054977
Tkinter和while True循环不兼容。 - Taku
如果您对我提供的代码细节有疑问,请随时询问。由于您的代码中有很多问题需要解决,所以解释它们可能会导致编写tkinter教程...希望您能用一段按您需求工作的代码来继续您的进展,并自行处理更多细节。 - Claudio
2个回答

2
运行以下代码并尝试理解它的作用和原理。当你按下显示的音符的按钮时,它会无限次地随机显示五个音符中的一个(否则它会打印出错误的选择,并等待你做出正确的选择)。我想这就是你所需要的。你自己的脚本尝试表明你需要投入一些工作来理解 tkinter 背后的基本机制。阅读注释以获取关于你自己编码方案有什么问题的提示。
请注意,你必须自己扩展字典以涵盖按钮覆盖的所有音符范围。
“隐藏”的功能是,如果你不喜欢显示的音符,你可以使用右箭头键切换到下一个音符 :D 。
from random import randint
from tkinter import *
import tkinter as tk
from PIL import ImageTk, Image

root = Tk()
root.wm_attributes("-topmost", 1)
root.geometry('{}x{}'.format(1100, 720)) # window size
# canvas = Canvas(root, bd=0, highlightthickness=0)
canvas = Canvas(root, width=950, height=700)
# canvas.pack()

def client_exit():
    ''' Quit Button '''
    exit()

def pickNote(value):
    ''' Changes noteChosen var to the note's button pressed '''
    global correctNote
    noteChosen = value 
    if noteChosen == correctNote:
        print("SUCCESS !!!")
        displayRandomNote(None)
    else:
        print( " :( ", noteChosen, " :( ")

# Creates button to exit the program
quitButton = tk.Button(text="Quit", command=client_exit)
quitButton.place(x=480, y=480)

# Creates buttons for various notes
aButton = tk.Button(text="A", command=lambda *args: pickNote("A"))
aButton.config(height=3, width=9)
aButton.place(x=190, y=400)

bButton = tk.Button(text="B", command=lambda *args: pickNote("B"))
bButton.config(height=3, width=9)
bButton.place(x=280, y=400)

cButton = tk.Button(text="C", command=lambda *args: pickNote("C"))
cButton.config(height=3, width=9)
cButton.place(x=370, y=400)

dButton = tk.Button(text="D", command=lambda *args: pickNote("D"))
dButton.config(height=3, width=9)
dButton.place(x=460, y=400)

eButton = tk.Button(text="E", command=lambda *args: pickNote("E"))
eButton.config(height=3, width=9)
eButton.place(x=550, y=400)

fButton = tk.Button(text="F", command=lambda *args: pickNote("F"))
fButton.config(height=3, width=9)
fButton.place(x=640, y=400)

gButton = tk.Button(text="G", command=lambda *args: pickNote("G"))
gButton.config(height=3, width=9)
gButton.place(x=730, y=400)

noteFiles = { 1:'1.png', 2:'2.png',  3:'3.png', 4:'4.png', 5:'5.png' } 
notes     = { 1:'A'    , 2:'B'    ,  3:'C'    , 4:'D'    , 5:'E'     } 

randomNote    = randint(1, 5)
path          = noteFiles[randomNote]
correctNote   = notes[randomNote]
img           = Image.open(path)
tk_img        = ImageTk.PhotoImage(img)
imageOnCanvas = canvas.create_image(130, 150, image=tk_img) # position of image center in window
canvas.pack()

def displayRandomNote(event):

    global canvas
    global imageOnCanvas
    global tk_img
    global correctNote
    global notes
    randomNote  = randint(1, 5)
    path        = noteFiles[randomNote]
    correctNote = notes[randomNote]
    img         = Image.open(path)
    tk_img      = ImageTk.PhotoImage(img)
    canvas.itemconfig(imageOnCanvas, image=tk_img) # change the displayed picture
    canvas.pack()

    # userResponse = input("Which note?\n           ")
    # if userResponse == correctNote:
    #     print("                      SUCCESS :) !!!")
    #     print("(switch focus)")
    # else:
    #     print("                      TRY ANOTHER ONE ...")
    #     print("(switch focus)")

# print("Switch window focus to CONSOLE to input the answer. ")
# print("Swicht window focus to IMAGE (press right arrow key for a Note)")

root.bind('<Right>', displayRandomNote) # on right arrow key display random note

root.mainloop()

附录:此程序中需要实现的下一步是,如果按下了正确的按钮,则让显示的音符播放。

1.png 2.png 3.png 4.png 5.png


我成功地将你所说的内容整合进来了, 但是我无法找到一种方法来检查你选择的音符是否是正确答案。我试图在displayRandomNote函数中实现这个功能,但是没有成功。 - user3054977
我曾尝试使用一次输入,但现在我会再试一次。为什么根据用户按下的按钮更改变量 noteChosen 不足以满足用户输入呢? - user3054977
它在某种程度上有效。它没有显示任何错误消息,但当我输入正确的注释时,它不会像应该的那样打印“成功”。如果可能的话,我仍然想像我的原始代码一样加入按钮。考虑到我总体上不擅长编程,我开始觉得这个项目有点超出我的能力范围了,哈哈。 - user3054977

1
GUI程序与普通的Python脚本完全不同。
GUI通常在事件循环中运行,处理鼠标点击、按键和其他合成事件。在tkinter中,这是mainloop()。在主循环设置之前执行的所有代码都是设置代码。
当主循环正在运行时,实际运行的只有您定义并附加到例如按钮按下和其他事件的回调函数。
因此,请将回调附加到按钮上。在该回调中,将所选注释与显示的注释进行比较。如果正确,则更新画布。如果不正确,则可能显示消息框。
请注意,当运行回调时,它会中断事件循环中的事件处理。因此,您的回调应该快速完成。如果您需要执行长时间运行的计算,可以将其分解为小块,并在超时事件处理程序(在tkinter中称为after)中执行这些块,或者您可以使用multiprocessing.Process在单独的进程中启动它。由于技术原因,在CPython中使用线程进行长时间运算效果不佳。
看着你发布的代码,不是逐个创建所有按钮,而是在循环中创建它们。我建议使用pack或(更好的选择)grid来放置按钮,而不是使用绝对定位。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接