Python (pygame/tkinter) MP3 Playlist - python

# -*- coding: utf-8 -*-
"""
"""
from tkinter import *
import pygame
from tkinter.messagebox import *
from tkinter import filedialog
#-------------------------------FONCTIONS---------------------------------
def importer_son(event):
file = filedialog.askopenfilename()
pygame.init()
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.mixer.init()
Liste_musique.append(file)
#TEST PRINT
print(file)
print (Liste_musique)
def stop_it(event):
pygame.mixer.music.pause()
global pause
pause = 1
def start_it(event):
global pause
if pause == 1:
pygame.mixer.music.unpause()
pause = 0
else:
try:
pygame.mixer.music.load(Liste_musique[index])
pygame.mixer.music.play()
except:
showwarning("Error","PAS DE FICHIER")
def close():
pygame.mixer.music.stop()
fen1.destroy()
def next_musique(event):
global index
index += 1
try:
pygame.mixer.music.load(Liste_musique[index])
pygame.mixer.music.play()
except:
showwarning("Error","PAS DE FICHIER")
def previous_musique(event):
global index
index -= 1
try:
pygame.mixer.music.load(Liste_musique[index])
pygame.mixer.music.play()
except:
showwarning("Error","PAS DE FICHIER")
def reint():
pygame.mixer.music.stop()
Liste_musique[:] = []
#-------------------------------Variables---------------------------------
Liste_musique = []
global index
index = 0
global pause
pause = 0
#-------------------------------MAIN---------------------------------
fen1 = Tk()
fen1.title("Lecteur Audio en python")
menubar = Menu(fen1)
menu1 = Menu(menubar, tearoff=0)
menu1.add_command(label="RĂ©initialiser", command=reint)
menu1.add_command(label="Quitter", command=close)
menubar.add_cascade(label="Option", menu=menu1)
fen1.config(menu=menubar)
can1 = Canvas(fen1, height=500, width=500)
can1.pack(side=LEFT, padx=5, pady=5)
fond_decran = PhotoImage(file="Background.gif")
can1.create_image(0,0, anchor = "nw", image = fond_decran)
can1.image = fond_decran
image_play = PhotoImage(file="Play.gif")
im_play = can1.create_image(50,400, anchor = "nw", image = image_play)
can1.image = image_play
image_stop = PhotoImage(file="stop.gif")
im_stop = can1.create_image(130,400, anchor = "nw", image = image_stop)
can1.image = image_stop
image_prev = PhotoImage(file="prev.gif")
im_prev = can1.create_image(210,400, anchor = "nw", image = image_prev)
can1.image = image_prev
image_next = PhotoImage(file="next.gif")
im_next = can1.create_image(290,400, anchor = "nw", image = image_next)
can1.image = image_next
image_importer = PhotoImage(file="importer.gif")
im_importer = can1.create_image(370,400, anchor = "nw", image = image_importer)
can1.image = image_importer
can1.tag_bind(im_play, '<Button-1>', start_it)
can1.tag_bind(im_stop, '<Button-1>', stop_it)
can1.tag_bind(im_next, '<Button-1>', next_musique)
can1.tag_bind(im_prev, '<Button-1>', previous_musique)
can1.tag_bind(im_importer, '<Button-1>', importer_son)
fen1.mainloop()
fen1.destroy()
In the code above I created my mp3 player, it works well but I would like to do a "playlist" : when a music is end go to the next or if there is only one music that it repeats it. To do that I already have a list with all songs (Liste_musique) and an index (index) to see where I am in the list. But I don't see how can I do that. I hope I could have explained well: D
Thanks for your help

Nae : See pygame.mixer.music.get_busy(), pygame.mixer.music.queue,
pygame.mixer.music.set_endevent, pygame.mixer.music.get_endevent()
Good answer

Related

tk.Misc.lift(canvas) seemingly not working

The full code for the chess game im making
import chess
#from stockfish import Stockfish
import tkinter as tk
import time
board = chess.Board()
def boardtoarr(board): #converts chess board string to 2d array
boardlist = []
linelist = []
for b in range(0,128,2):
linelist.append(str(board)[b])
if len(linelist) == 8:
boardlist.append(linelist)
linelist = []
return boardlist
#given the square of a piece on the board, returns the legal squares the piece can move to
def piecelegalmoves(square,cboard):
ans = []
for a in cboard.legal_moves:
a = str(a)
if a[0:2] == square:
ans.append(a[2::])
return ans
def alllegalmoves(cboard): #given a chess board it will return all legal moves
x = str(cboard.legal_moves)[38:-2]
x = x.split(', ')
return x
squarevar = [] #variable to contain the starting square and the moving-to square
def squareclicked(event):
global squarevar
x,y = event.x,event.y
#print(x,y)
#print(pixeltosquare(x,y))
#print(lsquaretocsquare(pixeltosquare(x,y)))
#squarevar.append(lsquaretocsquare(pixeltosquare(x,y)))
#print(squarevar)
return lsquaretocsquare(pixeltosquare(x,y))
def highlightlegalmoves(piece,board):
global BoardImage,redsquare
#for widget in root.winfo_children(): #code to clear page
#widget.destroy()
legalmoves = piecelegalmoves(piece,board)
canvas = tk.Canvas(root,width = BoardImage.width(), height = BoardImage.height())
#canvas = tk.Canvas(root,width = 500, height = 500)
canvas.pack()
for a in legalmoves:
x = csquaretolsquare(a)
print(x)
pixel = topleftpixels(x[0],x[1])
#canvas.create_image(0,0, image = redsquare, anchor = 'nw')
canvas.create_image(pixel[1],pixel[0], image = redsquare, anchor = 'nw')
tk.Misc.lift(canvas)
def makemoves(event): #Function to make chess moves when clicks have been inputted
global squarevar
squarevar.append(squareclicked(event))
if len(squarevar) != 2:
#print(squarevar[0])
highlightlegalmoves(squarevar[0],board)
if len(squarevar) == 2:
if squarevar[1] in piecelegalmoves(squarevar[0],board): #checking if move is legal to prevent errors (input validation)
board.push_san(squarevar[0]+squarevar[1]) #play move on backend board
squarevar = []
for widget in root.winfo_children(): #code to clear page
widget.destroy()
boardtoimage(root,str(board))
else:
squarevar = []
def topleftpixels(x,y): #returns top left pixel of given square
return [x*100,y*100]
def pixeltosquare(x,y): #given a pixel it will return what chess square it is on
x = ('000' + str(x))
x = x[len(x)-3::]
y = ('000' + str(y))
y = y[len(y)-3::]
return [int(y[0]),int(x[0])]
def csquaretolsquare(square): #converts chess square notation to list square notation
ans = []
letterorder = 'abcdefgh'
ans.append(8-int(square[1]))
ans.append(letterorder.index(square[0]))
return ans
def lsquaretocsquare(square): #converts list square notation to chess square notation
ans = ''
letterorder = 'abcdefgh'
ans += (letterorder[square[1]])
ans += str(8-square[0])
return ans
def boardtoimage(root,boardstr): #places all pieces onto the window graphically
global wQueen,wKing,wKnight,wBishop,wPawn,wRook,bQueen,bKing,bKnight,bBishop,bPawn,bRook,BoardImage
LetterToImage= {
'r': bRook, 'n': bKnight, 'b': bBishop, 'q': bQueen, 'k': bKing, 'p': bPawn,
'R': wRook, 'N': wKnight, 'B': wBishop, 'Q': wQueen, 'K': wKing, 'P': wPawn,
}
boardcanvas = tk.Canvas(root,width = BoardImage.width(), height = BoardImage.height()) #This will be the canvas for the whole board, pieces and all
boardcanvas.pack()
boardcanvas.create_image(0,0, image = BoardImage, anchor = 'nw') #placing chess board on canvas
#iterate through board and place piece images in correct place
for y in range(8):
for x in range(8):
piece = boardtoarr(boardstr)[y][x]
if piece in LetterToImage.keys():
boardcanvas.create_image(topleftpixels(x,y)[0], topleftpixels(x,y)[1], image=LetterToImage[piece], anchor="nw")
boolvar = False
def CMI_clicked(): #check if the chess menu image was clicked
global root
global BoardImage
global boolvar
for widget in root.winfo_children(): #code to clear page
widget.destroy()
boardtoimage(root,str(board))
root.bind("<Button-1>", makemoves)
def Menu():
global root
global ChessMenuImage
#Menu
ChessMenuOption = tk.Button(root ,image = ChessMenuImage, command = CMI_clicked) #create button using chess menu image and call function 'CMI_clicked' when pressed
ChessMenuOption.place(x = 380, y = 380) #place the chess menu image at given coordinates
root = tk.Tk()
ChessMenuImage = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\Chess_Selection_Image.png') #Load chess menu image file
BoardImage = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\ChessBoardImage.png') #Load board image file
redsquare = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\redsquare.png') #Load see-through red square file, used for showing legal moves in the GUI
#Piece image loading
#White
wQueen = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wQueen.png')
wKing = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wKing.png')
wPawn = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wPawn.png')
wBishop = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wBishop.png')
wKnight = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wKnight.png')
wRook = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\wRook.png')
#Black
bQueen = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bQueen.png')
bKing = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bKing.png')
bPawn = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bPawn.png')
bBishop = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bBishop.png')
bKnight = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bKnight.png')
bRook = tk.PhotoImage(file = r'C:\Users\benja\Documents\Python\Chess project\bRook.png')
root.geometry('800x800')
Menu()
root.mainloop()
specifically lines 47-61 'highlightlegalmoves' function
def highlightlegalmoves(piece,board):
global BoardImage,redsquare
#for widget in root.winfo_children(): #code to clear page
#widget.destroy()
legalmoves = piecelegalmoves(piece,board)
canvas = tk.Canvas(root,width = BoardImage.width(), height = BoardImage.height())
#canvas = tk.Canvas(root,width = 500, height = 500)
canvas.pack()
for a in legalmoves:
x = csquaretolsquare(a)
print(x)
pixel = topleftpixels(x[0],x[1])
#canvas.create_image(0,0, image = redsquare, anchor = 'nw')
canvas.create_image(pixel[1],pixel[0], image = redsquare, anchor = 'nw')
tk.Misc.lift(canvas)
so if I run the code
for widget in root.winfo_children(): #code to clear page
widget.destroy()
the image I get (before/after I click a piece)
before:
after (I clicked the e2 pawn):
so I know that under the original canvas its actually displaying what I want. I just what to show to lift it but its not doing that.
when I have the delete all widgets code commented, the image does not change once I click a piece, it does not display the legal moves of the piece.

How to allow Tkinter GUI to detect key presses when not selected

I am trying to make a full GUI, for A game borderlands 2, however it only detects my key presses when the tkinter box is selected (in focus). In a game this is not happening, so I need a way for tkinter to detect my key presses while not selected.
This is the code so far. It is not finished, ignore the "Auto Exec" and "Auto Exit" the problem is "Auto Reload". Clicking once will turn it ON and again will turn it OFF. Just choose any number (1-4) for the weapon slot, does not affect the error.
from easygui import *
from pyautogui import *
from time import *
import os
from tkinter import *
count = 1
slot = "0"
allowReload, allowExit = False, False
def poop():
sleep(3)
print("poop")
def countdown(count):
autoexecB.config(text = count)
if count == 2:
autoexecB.config(bg = "orange")
if count == 1:
autoexecB.config(bg = "yellow")
if count == 0:
autoexecB.config(text = "Running...", bg = "#44ff00")
if count > -1:
root.after(1000, countdown, count-1)
else:
for i in range(1, 3):
sleep(1)
press("'")
sleep(0.5)
type("exec Patch.txt")
press("enter")
press("esc")
press("enter")
sleep(4)
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(1)
press("'")
sleep(0.5)
type("exec STV.txt")
press("enter")
press("esc")
autoexecB.config(text = "Auto Exec", bg = "red")
def type(text):
typewrite(text)
def pressMult(key, amount):
for i in range(1, amount+1):
press(key)
def autoexec():
countdown(3)
def info():
msgbox("Auto Exec: Runs Mods automaticly\nAuto Exit: Exits the game automaticly to the main menu using INSERT\nAuto Reload: Automaticly reloads your gun using LEFT SHIFT")
def exit():
global count
count = 1
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(2.1)
press("enter")
if choose == "FARM at Hero's Pass":
sleep(3)
keyDown("w")
sleep(0.7)
keyUp("w")
keyDown("d")
sleep(1)
keyUp("d")
keyDown("ctrl")
sleep(0.5)
press("e")
keyUp("ctrl")
count += 1
def reloadslot():
global allowReload, slot
while True:
if allowReload == True:
break
slot = str(integerbox("Enter in the weapon's slot to be reloaded"))
if slot not in ("1","2","3","4"):
msgbox("A weapon can only be in slot 1, 2, 3 or 4")
else:
break
if allowReload == True:
allowReload = False
else:
allowReload = True
def on_press(event):
print(event.keysym)
if event.keysym == "Insert" and allowExit == True:
print("exit")
exit()
if event.keysym == "Shift_L" and allowReload == True:
print("running reload")
press(";")
press("e")
press(slot)
print("done")
root = Tk()
root.bind('<KeyPress>', on_press)
root.geometry("378x134")
root.config(bg = "blue")
autoexecB = Button(text = "Auto Exec", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: autoexec())
autoexitB = Button(text = "Auto Exit", bg = "red", font = ("calibri","13"), height = 3, width = 13)
autoreloadB = Button(text = "Auto Reload", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: reloadslot())
infoB = Button(text = "INFO", bg = "blue", width = 26, height = 3, command = lambda: info())
exitB = Button(text = "EXIT", bg = "blue", width = 25, height = 3, command = lambda: root.destroy())
autoexecB.place(x = 0, y = 0)
autoexitB.place(x = 126, y = 0)
autoreloadB.place(x = 252, y = 0)
infoB.place(x = 0, y = 78)
exitB.place(x = 193, y = 78)
root.mainloop()
root.mainloop()
I need a way for tkinter to detect my key presses while not selected
You can't use tkinter for that. It can only detect keyboard events when it has the keyboard focus. You will have to use some platform-specific library to do what you want.

Python tkinter canvas transparent

I am looking to make the background of the tkinter canvas transparent, but still have Mouse events of the canvas, here is my code, I am on Windows 10, Python 3.6:
from tkinter import *
import time
WIDTH = 500
HEIGHT = 500
LINEWIDTH = 1
TRANSCOLOUR = 'gray'
global old
old = ()
tk = Tk()
tk.title('Virtual whiteboard')
tk.wm_attributes('-transparentcolor', TRANSCOLOUR)
canvas = Canvas(tk, width=WIDTH, height=HEIGHT)
canvas.pack()
canvas.config(cursor='tcross')
canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=TRANSCOLOUR, outline=TRANSCOLOUR)
def buttonmotion(evt):
global old
if old == ():
old = (evt.x, evt.y)
return
else:
canvas.create_line(old[0], old[1], evt.x, evt.y, width=LINEWIDTH)
old = (evt.x, evt.y)
def buttonclick(evt):
global old
canvas.create_line(evt.x-1, evt.y-1, evt.x, evt.y, width=LINEWIDTH)
old = (evt.x, evt.y)
canvas.bind('<Button-1>', buttonmotion)
canvas.bind('<B1-Motion>', buttonclick)
while True:
tk.update()
time.sleep(0.01)
When run the code, it makes a transparent background, but I select the things under, instead of the canvas.
I build a little workaround with the help of the win api, here is my suggestion:
from tkinter import *
import time
import win32gui
import win32api
WIDTH = 500
HEIGHT = 500
LINEWIDTH = 1
TRANSCOLOUR = 'gray'
title = 'Virtual whiteboard'
global old
old = ()
global HWND_t
HWND_t = 0
tk = Tk()
tk.title(title)
tk.lift()
tk.wm_attributes("-topmost", True)
tk.wm_attributes("-transparentcolor", TRANSCOLOUR)
state_left = win32api.GetKeyState(0x01) # Left button down = 0 or 1. Button up = -127 or -128
canvas = Canvas(tk, width=WIDTH, height=HEIGHT)
canvas.pack()
canvas.config(cursor='tcross')
canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=TRANSCOLOUR, outline=TRANSCOLOUR)
def putOnTop(event):
event.widget.unbind('<Visibility>')
event.widget.update()
event.widget.lift()
event.widget.bind('<Visibility>', putOnTop)
def drawline(data):
global old
if old !=():
canvas.create_line(old[0], old[1], data[0], data[1], width=LINEWIDTH)
old = (data[0], data[1])
def enumHandler(hwnd, lParam):
global HWND_t
if win32gui.IsWindowVisible(hwnd):
if title in win32gui.GetWindowText(hwnd):
HWND_t = hwnd
win32gui.EnumWindows(enumHandler, None)
tk.bind('<Visibility>', putOnTop)
tk.focus()
running = 1
while running == 1:
try:
tk.update()
time.sleep(0.01)
if HWND_t != 0:
windowborder = win32gui.GetWindowRect(HWND_t)
cur_pos = win32api.GetCursorPos()
state_left_new = win32api.GetKeyState(0x01)
if state_left_new != state_left:
if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
drawline((cur_pos[0] - windowborder[0] - 5, cur_pos[1] - windowborder[1] - 30))
else:
old = ()
except Exception as e:
running = 0
print("error %r" % (e))
Shot explanation of the new code bits:
tk.lift()
tk.wm_attributes("-topmost", True)
...
def putOnTop(event):
event.widget.unbind('<Visibility>')
event.widget.update()
event.widget.lift()
event.widget.bind('<Visibility>', putOnTop)
...
tk.bind('<Visibility>', putOnTop)
tk.focus()
These lines ensure, that the window will be always be on top of all other windows.
global HWND_t
HWND_t = 0
...
def enumHandler(hwnd, lParam):
global HWND_t
if win32gui.IsWindowVisible(hwnd):
if title in win32gui.GetWindowText(hwnd):
HWND_t = hwnd
win32gui.EnumWindows(enumHandler, None)
This code bit will go through all the windows currently displayed and catches the handle of the whiteboard window (make sure the title is unique, or this could capture the wrong handle).
state_left = win32api.GetKeyState(0x01)
...
if HWND_t != 0:
windowborder = win32gui.GetWindowRect(HWND_t)
cur_pos = win32api.GetCursorPos()
state_left_new = win32api.GetKeyState(0x01)
if state_left_new != state_left:
if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
drawline((cur_pos[0] - windowborder[0] - 5, cur_pos[1] - windowborder[1] - 30))
else:
old = ()
This
Checks, if the handle is found
Checks, if mouse button 1 is clicked or not
Checks, if mouse is inside the window
if all is true, it takes the mouse data and draws the line
the current mode is, that it doesn't draw anything till the button is clicked and then draws until the button is clicked again.
I'm sure you've thought of this, but have you tried setting the hexadecimal colour as ""?
i.e
canvas = tk.Canvas(width, height, bg = "")
That works for my version of python.

Tkinter: Can't add more than 3 PhotoImages to a list for animation?

I'm trying to program a game in Tkinter (trust that I'd much rather use another better-suited library), and I'm having trouble coding in animations. I tried 2 ways of working around the problem but both have failed.
What I've done is to write an Animation class, which, in my first implementation, takes a list of PhotoImages, and deletes/draws each frame:
class Animation():
def __init__(self, container, pos, images, framerate):
self.container = container
self.x = pos[0]
self.y = pos[1]
self.images = copy(images)
self.imagescopy = copy(images)
self.framerate = framerate # ms between frames
self.done = False
def animate(self):
if len(self.images)>0:
self.container.delete("currentframe")
self.container.create_image(self.x, self.y, image = self.images.pop(0), tag = "currentframe")
self.animation = self.container.after(self.framerate, self.animate)
else:
self.container.delete("currentframe")
self.container.after_cancel(self.animation)
self.done = True
In the init method of the class which draws the Tk window, I loaded the PhotoImages (I tried this with a simple placeholder 5 frame animation):
for i in range(1,6):
self.imgs_playbgready.append(PhotoImage(file ='Graphics\\PlayArea\\ani_bgready'+str(i)+'.gif'))
But when I try to execute the program, it takes very very long to load beyond the third image. Even writing out each line explicitly like so:
self.imgs_playbgready = [PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready1.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready2.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready3.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready4.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready5.gif') ]
didn't help. Removing the last 2 items seems to make everything fine, so it looks like there's a limit on 3 PhotoImages in a list though I can't imagine why that'd be the case?
In my second implementation, I tried to change the animate method so that the PhotoImages would be 'lazily loaded' (so now self.images is a list of file names rather than PhotoImages):
def animate(self):
if len(self.images)>0:
self.container.delete("currentframe")
image = PhotoImage(file = self.images.pop(0))
self.container.create_image(self.x, self.y, image = image, tag = "currentframe")
self.animation = self.container.after(self.framerate, self.animate)
else:
self.container.delete("currentframe")
self.container.after_cancel(self.animation)
self.done = True
Now when I try to run the program it freezes at frame 4 and I have to force quit. Again, it seems 3 is the magic number?
I'm not really sure at all what's going on with this. From googling about animation in Tkinter, redrawing PhotoImages frame by frame seems the way to go. Please be patient with me because I only started learning to use the Tkinter library last week. Any help is much appreciated.
EDIT
Thanks for your help.
To clarify the points raised in the comment (full working code at the end):
It takes a few minutes before the last two images load in and the program starts up. My framerate as defined in the Animation class is more like an inverse framerate (number of miliseconds between each redraw) as you can see in the animate function. My whole code is really quite long so I will try to extract the more relevant parts (for the first implementation I tried). Basically in my GUI class (named PlayArea), under the __init__ method, I try to load in the image list for the animation as well as create an Animation instance:
class PlayArea():
def __init__(self):
self.window = Tk()
.....
#---------------------
# Button and background images
#---------------------
......
self.imgs_playbgready = [PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready1.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready2.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready3.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready4.gif'),
PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready5.gif') ]
.......
self.ani_playbgready = Animation(self.playCanvas,(self.playWidth/2, self.playHeight/2),self.imgs_playbgready, 5000)
The animation will only run after a user clicks a button, and the command callback for the button is this(I've put in only the relevant portions):
startGame(self):
# Play Area
self.playCanvas.delete("bgready")
self.ani_playbgready.animate() # animating the images
I know for sure that the program is freezing up after loading the third PhotoImage, because when I try to do the loading using a for loop, and print the list after each loop, I can see it hang up in the fourth one. As mentioned, this code works as long as I only load 3 images into the list. In other words, it probably isn't a problem with my animate method
The monstrous block of working code as requested:
from Tkinter import *
import tkFont
from PIL import Image, ImageTk
from copy import deepcopy, copy
#------------------------------------------------
# Player, Controller, Levels, and Customer
#------------------------------------------------
class Player():
'''
Player class to keep track of progress of specific player.
Progress and highscore will be written to a save file
'''
def __init__(self, name = "Default", highscore = 0, currentlevel = 1):
self.name = name
self.highscore = highscore
self.currentlevel = currentlevel
class Controller():
'''
Controller which keeps track of score as well as the timer.
To be instantiated by the GUI class to determine the score and time left to display
'''
def __init__(self):
self.score = 0
self.level = 1
self.timer = 10
def runTimer(self):
if self.timer > 0:
self.timer -= 1
else:
self.timer = 10
class Levels():
'''
Contains the commands(tasks) for each level, as well as the order of customers and the
corresponding answers.
To be instantiated by the GUI class to determine the current command and customer to display
'''
def __init__(self):
#--------------------------------------------------------------
# Initialize commands, anwers and customers for each level (hardcode for now, may be a way to
# randomize or dynamically generate challenges depending on the level design)
# Customers can have more than one attribute, so we store it as a tuple
# Answers for each customer can be more than one pizza (eg. for a while loop), so store it as a tuple
# If the statement doesn't execute, correct answer is to move on to the next customer
#---------------------------------------------------------------
# Level 1
# Action images
self.img_pepperoni = (PhotoImage(file = 'Graphics\\Actions\\pepperoniup.gif'),
PhotoImage(file = 'Graphics\\Actions\\pepperonihover.gif'),
PhotoImage(file = 'Graphics\\Actions\\pepperonidown.gif'))
self.img_hawaiian = (PhotoImage(file = 'Graphics\\Actions\\hawaiianup.gif'),
PhotoImage(file = 'Graphics\\Actions\\hawaiianhover.gif'),
PhotoImage(file = 'Graphics\\Actions\\hawaiiandown.gif'))
self.img_vegetarian = (PhotoImage(file = 'Graphics\\Actions\\vegetarianup.gif'),
PhotoImage(file = 'Graphics\\Actions\\vegetarianhover.gif'),
PhotoImage(file = 'Graphics\\Actions\\vegetariandown.gif'))
self.img_nextcustomer = (PhotoImage(file = 'Graphics\\Actions\\nextcustomerup.gif'),
PhotoImage(file = 'Graphics\\Actions\\nextcustomerhover.gif'),
PhotoImage(file = 'Graphics\\Actions\\nextcustomerdown.gif'))
level1commands = ['if customer_is_red:\n\tserve pepperoni', 'if customer_is_blue:\n\tserve hawaiian']
level1customers = [('red'), ('green')] #make this a dict corresponding to image too?
level1answers = [('pepperoni'), ('next')]
level1actions = {'pepperoni': self.img_pepperoni , 'hawaiian':self.img_hawaiian, 'vegetarian': self.img_vegetarian,
'next': self.img_nextcustomer}
self.levelscommands = [level1commands]
self.levelscustomers = [level1customers]
self.levelsanswers = [level1answers]
self.levelsactions = [level1actions]
class Customer():
def __init__(self, **attributes):
for k,v in attributes:
self.k = v
#--------------------------------
# Animations
#--------------------------------
class Animation():
def __init__(self, container, pos, images, framerate):
self.container = container
self.x = pos[0]
self.y = pos[1]
self.images = copy(images)
self.imagescopy = copy(images)
self.framerate = framerate # ms between frames
self.done = False
def animate(self):
if len(self.images)>0:
print "animating", len(self.images), self.images
self.container.delete("currentframe") # won't throw an error if non-existant
print self.images[0], "image name"
image = PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready1.gif')
print image, "image"
self.container.create_image(self.x, self.y, image = image, tag = "currentframe")
self.animation = self.container.after(self.framerate, self.animate)
else:
print "finished"
self.container.delete("currentframe")
self.container.after_cancel(self.animation)
self.done = True
def resetAnimation(self):
self.done = False
self.images = copy(self.imagescopy)
#-------------------------
# GUI classes
#-------------------------
class PlayArea():
def __init__(self):
self.window = Tk()
self.window.title("Pizza Program!")
#------------------------------------------------------------
# Initialize player, controller, levels and customer classes
#------------------------------------------------------------
self.player = Player()
self.controller = Controller()
self.levels = Levels()
self.customer = Customer()
#---------------------
# Button and background images
#---------------------
self.img_buttonstart = (PhotoImage(file = 'Graphics\\Controller\\button_startup.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_starthover.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_startdown.gif'))
self.img_buttonpause = (PhotoImage(file = 'Graphics\\Controller\\button_pauseup.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_pausehover.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_pausedown.gif'))
self.img_buttonresume = (PhotoImage(file = 'Graphics\\Controller\\button_resumeup.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_resumehover.gif'),
PhotoImage(file = 'Graphics\\Controller\\button_resumedown.gif'))
self.img_playbgplay = Image.open("Graphics\\PlayArea\\bgplay.gif")
self.img_playbgready = Image.open("Graphics\\PlayArea\\bgready.gif")
# self.imgs_playbgready = [PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready1.gif'),
# PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready2.gif'),
# PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready3.gif'),
# PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready4.gif'),
# PhotoImage(file = 'Graphics\\PlayArea\\ani_bgready5.gif') ]
#-----------------------
# Animations
#-----------------------
self.imgs_playbgready = []
self.imgs_startserving = []
for i in range(1,4):# change later
self.imgs_playbgready.append('Graphics\\PlayArea\\ani_bgready'+str(i)+'.gif')
self.imgs_startserving.append('Graphics\\PlayArea\\ani_startserving'+str(i)+'.gif')
#self.imgs_playbgready = []
#for filename in imgs_playbgready:
# self.imgs_playbgready.append(PhotoImage(file = filename))
#--------------
# Font styles
#--------------
self.controlFont = tkFont.Font(family = "Calibri", size = 10, weight = "bold" )
self.commandFont = tkFont.Font(family = "Courier New", size = 14, weight = "bold")
#-------------------------------------------------------------------------------------------
# Frames to contain the play area (graphics), the command area (what players must do),
# the controller area (start button, timer, score), and action area (possible actions players can take)
#-------------------------------------------------------------------------------------------
self.playFrame = Frame(self.window, bd = 0, highlightthickness = 0)
self.commandFrame = Frame(self.window, bd =0, highlightthickness =0)
self.controlFrame = Frame(self.window, bd =0, highlightthickness =0)
self.actionsFrame = Frame(self.window, bd =0, highlightthickness =0)
self.commandFrame.grid(column = 1, row = 1)
self.playFrame.grid(column = 1, row = 2)
self.controlFrame.grid(column = 2, row = 1)
self.actionsFrame.grid(column = 2, row = 2)
self.actionsFrame.pack_propagate(False)
#self.actionsFrame.columnconfigure(1,weight=1)
#self.window.columnconfigure(2, weight =1)
#-----------------------------------
# Play Area Elements
#-----------------------------------
self.playWidth = 500
self.playHeight = 500
self.playCanvas = Canvas(self.playFrame, width = self.playWidth, height = self.playHeight, bd = -2)
self.playCanvas.pack()
resized = self.img_playbgplay.resize((self.playWidth, self.playHeight), Image.ANTIALIAS)
self.img_playbgplay = ImageTk.PhotoImage(resized)
resized = self.img_playbgready.resize((self.playWidth, self.playHeight), Image.ANTIALIAS)
self.img_playbgready = ImageTk.PhotoImage(resized)
self.playCanvas.create_image(self.playWidth/2, self.playHeight/2, image = self.img_playbgplay)
self.playCanvas.create_image(self.playWidth/2, self.playHeight/2, image = self.img_playbgready,tag = "bgready")
# Bgready animation for when start is pressed
self.ani_playbgready = Animation(self.playCanvas,(self.playWidth/2, self.playHeight/2),self.imgs_playbgready, 5000)
self.ani_startserving = Animation(self.playCanvas,(self.playWidth/2, self.playHeight/2), self.imgs_playbgready,5000)
# Controller elements
# timer and score labels only appear after game starts
self.controlColor = "sky blue"
self.controlWidth = 200
self.controlHeight = 200
self.controllerCanvas = Canvas(self.controlFrame, width = self.controlWidth, height = self.controlHeight, bg = self.controlColor,
bd =0 , highlightthickness =0)
# Frame for containing button, timer and score
self.controlLabelFrame = LabelFrame(self.controllerCanvas, bd = 0, highlightthickness = 0, relief = "ridge", bg = self.controlColor)
#print self.controlLabelFrame.config(), "configs for controlLabelFrame"
self.btStart = Button(self.controlLabelFrame, command = self.startGame, highlightthickness =0, bd =0,
bg = self.controlColor, image = self.img_buttonstart[0])
self.btPause = Button(self.controlLabelFrame, command = self.pauseGame, highlightthickness =0, bd =0,
bg = self.controlColor, image = self.img_buttonpause[0])
self.btResume = Button(self.controlLabelFrame, command = self.resumeGame, highlightthickness =0, bd =0,
bg = self.controlColor, image = self.img_buttonstart[0])
self.controllerCanvas.pack()
self.btStart.grid(row=1, pady = 5)
self.controllerCanvas.create_window(self.controlWidth/2, self.controlHeight/2, window = self.controlLabelFrame)
#Toggle button image
self.changeButtonImage(self.btStart, self.img_buttonstart)
# Timer label
self.timerText = StringVar()
self.timerLabel = Label(self.controlLabelFrame, textvariable = self.timerText, bg = self.controlColor, font = self.controlFont)
self.timerLabel.grid( row = 2, pady=5)
# Level label
self.levelText = StringVar()
self.levelLabel = Label(self.controlLabelFrame, textvariable = self.levelText, bg = self.controlColor, font = self.controlFont)
self.levelLabel.grid( row = 3, pady=5)
# Command elements
self.commandColor = "light sky blue"
self.commandWidth = 500
self.commandHeight = 200
self.commandCanvas = Canvas(self.commandFrame, width = self.commandWidth, height = self.commandHeight, bg = self.commandColor)
self.commandCanvas.pack()
self.commText = StringVar()
self.commLabel = Label(self.commandCanvas, textvariable = self.commText, bg = self.commandColor, font = self.commandFont)
self.commandCanvas.create_window(self.commandWidth/2, self.commandHeight/2, window = self.commLabel)
# Action elements
self.actionColor = "cornflower blue"
self.actionWidth = 200
self.actionHeight = 500
self.actionsFrame.config(width = self.actionWidth, height = self.actionHeight, bg = self.actionColor,)
#self.actionCanvas = Canvas(self.actionsFrame, width = self.actionWidth, height = self.actionHeight, bg = self.actionColor, highlightthickness =0)
#self.actionCanvas.pack()
self.window.mainloop()
#-------------------------------------
# Step methods
# (for checking conditions each step)
#-------------------------------------
def checkAnimation(self, animation, triggerevent, *args):
if not animation.done:
self.window.after(100, self.checkAnimation)
else:
triggerevent(args)
#-------------------------------------
# Controller methods
#-------------------------------------
def changeButtonImage(self, button, images):
def toImage(event, button, image):
button["image"] = image
up, hover, down = images
button.bind("<Enter>", lambda event, button = button, image = hover : toImage(event, button, image))
button.bind("<Leave>", lambda event, button = button, image = up : toImage(event, button, image))
button.bind("<Button-1>", lambda event, button = button, image = down : toImage(event, button, image))
button.bind("<ButtonRelease-1>", lambda event, button = button, image = up : toImage(event, button, image))
def updateController(self):
self.timerText.set("Next Customer: " + str(self.controller.timer))
self.controller.runTimer()
self.update = self.controlFrame.after(1000, self.updateController)
def startGame(self):
#--------------------------
# Update controller elements
#-----------------------------
# Change button
self.btStart.grid_remove()
self.btPause.grid(row=1, pady = 5)
self.changeButtonImage(self.btPause, self.img_buttonpause)
# Timer label
self.timerText.set("Next Customer:" + str(self.controller.timer))
# Level label
self.levelText.set("Level: " + str(self.controller.level))
self.updateController()
#--------------------------------
# Access level commands, customers, answers, and set command display
#--------------------------------
# Commands
self.levelcommands = deepcopy(self.levels.levelscommands[self.controller.level-1]) #copy the list
self.currentcommand = self.levelcommands.pop(0)
self.displayCommand(self.currentcommand)
# Actions
self.levelactions = copy(self.levels.levelsactions[self.controller.level-1])
self.btActions = []
for action, img in self.levelactions.iteritems():
if action != "next":
self.btActions.append(Button(self.actionsFrame, command = self.servePizza,
highlightthickness =0, bd =0, bg = self.actionColor, image = img[0]))
else:
self.btActions.append(Button(self.actionsFrame, command = self.nextCustomer,
highlightthickness =0, bd =0, bg = self.actionColor, image= img[0]))
index = self.btActions.index(self.btActions[-1]) +1
self.btActions[-1].pack(pady = 35)
self.changeButtonImage(self.btActions[-1], img)
# Play Area
self.playCanvas.delete("bgready")
self.ani_playbgready.animate()
#self.checkAnimation(self.ani_playbgready, self.ani_startserving.animate)
self.levelcustomers = deepcopy(self.levels.levelscustomers[self.controller.level-1]) #copy the list
self.currentcustomer = self.levelcustomers.pop(0)
#self.displayCustomer(self.currentcustomer) #need to write this method
# Answers
self.levelanswers = self.levels.levelsanswers[self.controller.level-1][:] # no nested elements
self.currentanswer = self.levelanswers.pop(0)
def pauseGame(self):
#Pause controller updating
# Change button
self.btPause.grid_remove()
self.btResume.grid(row=1, pady = 5)
self.changeButtonImage(self.btResume, self.img_buttonresume)
self.controlFrame.after_cancel(self.update)
def resumeGame(self):
# Resume controller updating
# Change button
self.btResume.grid_remove()
self.btPause.grid(row=1, pady = 5)
self.changeButtonImage(self.btResume, self.img_buttonpause)
self.updateController()
def readyLevel(self):
pass
#---------------------------------
# Command methods
#---------------------------------
def updateCommand(self):
pass
def displayCommand(self, command):
self.commText.set(command)
#--------------------------------
# Action methods
#--------------------------------
def servePizza(self):
pass
def nextCustomer(self):
pass
PlayArea()

Tkinter Image Viewer Method

I am an atmospheric scientist, struggling through python (2.7 using PIL and Tkinter) in an attempt to create a simple image viewer that will display some of the forecast products we eventually produce. I would like to implement a start, stop, forward, backward, and loop button. The loop button and its associated callback currently work correctly. The loop method is credited to Glenn Pepper (found an open-source project via google).
Here is the code:
from Tkinter import *
from PIL import Image, ImageTk
import tkMessageBox
import tkFileDialog
#............................Button Callbacks.............................#
root = Tk()
root.title("WxViewer")
root.geometry("1500x820+0+0")
def loop():
StaticFrame = []
for i in range(0,2):
fileName="filename"+str(i)+".gif"
StaticFrame+=[PhotoImage(file=fileName)]
def animation(currentframe):
def change_image():
displayFrame.create_image(0,0,anchor=NW,
image=StaticFrame[currentframe], tag='Animate')
# Delete the current picture if one exist
displayFrame.delete('Animate')
try:
change_image()
except IndexError:
# When you get to the end of the list of images -
#it simply resets itself back to zero and then we start again
currentframe = 0
change_image()
displayFrame.update_idletasks() #Force redraw
currentframe = currentframe + 1
# Call loop again to keep the animation running in a continuous loop
root.after(1000, animation, currentframe)
# Start the animation loop just after the Tkinter loop begins
root.after(10, animation, 0)
def back():
print("click!")
def stop():
print("click!")
def play():
print("click!")
def forward():
print("click!")
#..........................ToolFrame Creation............................#
toolFrame = Frame(root)
toolFrame.config(bg="gray40")
toolFrame.grid(column=0,row=0, sticky=(N,W,E,S) )
toolFrame.columnconfigure(0, weight = 1)
toolFrame.rowconfigure(0, weight = 1)
toolFrame.pack(pady = 0, padx = 10)
backButton = Button(toolFrame, text="Back", command = back)
backButton.pack(side = LEFT)
stopButton = Button(toolFrame, text = "Stop", command = stop)
stopButton.pack(side = LEFT)
playButton = Button(toolFrame, text = "Play", command = play)
playButton.pack(side = LEFT)
forwardButton = Button(toolFrame, text = "Forward", command = forward)
forwardButton.pack(side = LEFT)
loopButton = Button(toolFrame, text = "Loop", command = loop)
loopButton.pack(side = LEFT)
toolFrame.pack(side = TOP, fill=X)
#........................DisplayFrame Creation..........................#
displayFrame = Canvas(root, width=1024,height=768)
displayFrame.config(bg="white")
displayFrame.grid(column=0,row=0, sticky=(N,W,E,S) )
displayFrame.columnconfigure(0, weight = 1)
displayFrame.rowconfigure(0, weight = 1)
displayFrame.pack(pady = 5, padx = 10)
displayFrame.pack(side = LEFT, fill=BOTH)
#...............................Execution...............................#
root.mainloop()
It is rather straightforward. I have searched GitHub for projects, google, and stackoverflow but I am unsure how to make these five methods play nicely with each other. Could anyone share some pointers on how I could construct these other four methods so that they work appropriately? Thank you for your time!
I would replace the current loop function with something like the following (untested). The changes: add some global names to share data between functions; make change_image do everything needed to change an image, given that current_image is valid; except for the starting value, make current_image be always a valid image number when it is changed; factor forward() out of animate() (which is just repeated times forward calls).
n_images = 2
images = [PhotoImage(file="filename"+str(i)+".gif") for i in range(n_images)]
current_image = -1
def change_image():
displayFrame.delete('Animate')
displayFrame.create_image(0,0, anchor=NW,
image=StaticFrame[current_image], tag='Animate')
displayFrame.update_idletasks() #Force redraw
callback = None
def animate():
global callback
forward()
callback = root.after(1000, animate)
Here are three of the other functions.
def forward():
global current_image
current_image += 1
if current_image >= n_images:
current_image = 0
change_image()
def back():
global current_image
current_image -= 1
if current_image < 0:
current_image = n_images-1
change_image()
def stop():
if callback is not None:
root.after_cancel(callback)

Categories