Changing image in tkinter canvas in while loop

2019-06-25 09:56发布

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

2条回答
姐就是有狂的资本
2楼-- · 2019-06-25 10:23

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.

查看更多
做自己的国王
3楼-- · 2019-06-25 10:46

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.

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

查看更多
登录 后发表回答