在 while 循环中更改 tkinter canvas 中的图像

Changing image in tkinter canvas in while loop

我的全部代码是 here

使用 tkinter 的 canvas,我正在尝试创建一个小游戏,让人们练习学习高音谱号上的音符。

最初显示随机注释,用户必须 select 正确的注释。如果学生得到正确答案,我无法编辑 canvas 以显示新笔记的图像。

有人可以解释一下我的代码有什么问题吗?

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")

运行 下面的代码并尝试理解它的作用和方式。当您按下所显示音符的按钮时,它会一次又一次地显示随机 "note" 五分之二(否则它会打印出错误的选择音符并等待您正确选择)。我想这就是你所需要的。您自己的脚本编写尝试表明您必须花一些功夫来理解 tkinter 背后的基本机制。阅读评论以提示您自己的编码尝试出了什么问题。

请注意,您必须自己扩展词典才能使按钮涵盖所有范围的注释。

"hidden" 的特点是,如果您不喜欢显示的音符,可以使用向右箭头键切换到下一个音符: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()

附录:此程序中接下来要实现的是,如果按下右侧的一个按钮,则让显示的音符播放。

GUI 程序与普通 Python 脚本完全不同

GUI 通常 运行 在一个事件循环中,处理鼠标点击、按键和其他合成事件。在 tkinter 中,这是 mainloop()。在主循环之前执行的所有代码都是设置代码。

当主循环是 运行ning 时,您的代码中唯一真正 运行 的部分是您定义和附加的 callback 函数例如按钮按下和其他事件。

因此将回调附加到按钮。在该回调中,将所选音符与显示的音符进行比较。如果正确,请更新 canvas。如果不正确,可能会显示一个消息框。

请注意,当您的回调正在 运行 时,它会中断事件循环中事件的处理。所以你的回调应该很快完成。如果你需要执行一个长 运行ning 计算,你可以将它分成小块并在超时事件处理程序中执行(在 tkinter 中称为 after),或者你可以在一个单独的进程中启动它使用 multiprocessing.Process。由于技术原因,使用 threading 进行 long-运行ning 计算确实 not 在 CPython.

中工作得很好

查看您发布的代码,您不是单独创建所有按钮,而是循环创建它们。我建议使用 pack 或(最好)grid 来放置按钮而不是使用绝对位置。