问题
Entirety of my code is here.
Using tkinter's canvas, I am trying to create a small game that allows people to practice learning the notes on the treble clef.
A random note is initially displayed and the user must select the correct note. I am unable to edit the canvas to display a new note's image if the student got the correct answer.
Can somebody please explain what the problem is with my code?
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")
回答1:
Run the code below and try to understand what it does and how. It displays a random "note" out of five again and again when you press the button for the note displayed (else it prints the bad chosen note and waits until you get it right). I suppose that this is what you need. Your own scripting attempt shows that you have to put some work into your understanding of the basic mechanisms behind tkinter. Read the comments for hints what was wrong with your own coding attempt.
Notice the you have to extend the dictionaries yourself to have the full range of notes covered by the buttons.
The "hidden" feature is that you can switch with the right arrow key to the next note if you don't like the one displayed :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()
ADDENDUM: The next thing to implement in this program is to let the displayed note play if the right one button is pressed.
回答2:
GUI programs work completely different from normal Python scripts.
GUI's generally run in an event loop, processing mouse clicks, keys being pressed and other synthetic events. In tkinter this is the mainloop()
. All code that is executed before the main loop is setup code.
When the main loop is running, the only pieces of your code that are actually run are callback functions that you have defined and attached to e.g. button presses and other events.
So attach a callback to the buttons. In that callback, compare the chosen note to the shown one. If correct, update the canvas. If not correct, maybe show a message box.
Note that when your callback is being run, it interrupts the processing of the events in the event loop. So your callbacks should finish quickly. If you need to perform a long-running calculation you could chop it up into little pieces and execute those in a timeout event handler (called after
in tkinter) or you could start it in a separate process using a multiprocessing.Process
. For technical reasons, using threading
for long-running computations does not work well in CPython.
Looking at the code you posted, instead of creating all the buttons individually, you create them in aloop. And I would suggest to use pack
or (preferably) grid
to place buttons instead of using absolute placement.
来源:https://stackoverflow.com/questions/43691102/changing-image-in-tkinter-canvas-in-while-loop