Tkinter not letting me insert a text in a Text Widget - python

from tkinter import *
from PIL import Image, ImageTk
import time
schermata = Tk()
screen_width = schermata.winfo_screenwidth()
screen_height = schermata.winfo_screenheight()
indice = 0
schermata.iconbitmap("immagini\icona.ico")
screen_resolution = str(screen_width)+'x'+str(screen_height)
large_font = ('Verdana',30)
schermata.geometry(screen_resolution)
schermata.title("Jovan's RPG")
class GUI(Frame):
def __init__(self, master):
super(GUI, self).__init__(master)
self.pack()
self.bg()
self.immagine()
self.testo()
self.statistiche()
self.inserimenti()
def bg(self):
load = Image.open("immagini\\background.png")
render = ImageTk.PhotoImage(load)
img = Label(schermata, image = render)
img.image = render
img.pack()
def immagine(self):
load = Image.open("immagini\\dn.png")
render = ImageTk.PhotoImage(load)
img = Label(schermata, image = render)
img.image = render
img.place( x = 10, y = 10 )
def testo(self):
self.testo = Text(schermata, width = 110, height = 35, border = 5, bg = "black", fg ="white")
self.testo.place( x = 400, y = 20 )
def statistiche(self):
self.stats = Text(schermata, width = 40, height = 10, border = 5, bg = "black", fg ="white")
self.stats.place( x = 10, y = (screen_height - 200))
def inserisci(self):
fraseInserita = self.inserimento.get()
scrivere(fraseInserita)
self.inserimento.delete('0', END)
def inserimenti(self):
self.inserimento = Entry(schermata,font=large_font, width = 25, border = 5, bg = "black", fg ="white")
self.inserimento.place( x = 400, y = (screen_height - 100))
self.bottone = Button(schermata, width = 30, height = 3, border = 5, text = "Inserisci", command = self.inserisci)
self.bottone.place( x = (screen_width - 300), y = (screen_height - 100))
g = GUI(schermata)
def scrivere(scrittura):
g.testo.insert('1.0', scrittura)
def cancellaTesti():
g.testo.delete('0',END)
def wait(secondi):
time.sleep(secondi)
Levels class
from GUI import *
g = GUI(schermata)
class Livelli():
def __init__(self): pass
def cicloLivelli(self):
self.presentazione()
def presentazione(self):
scrivere("Salve avventuriero, qual e' il tuo nome?")
Main
from GUI import *
a = GUI(schermata)
l = Livelli()
if __name__ == "__main__":
a.mainloop()
l.cicloLivelli()
As you see i called the function back[(scrivere)], but the interpreter won't let the string appear in the Text widget. I've just posted the class of the GUI and the class of the "levels" that i'm looking forward to use for creating, of course, my levels for the game i'm creating. I'm searching for an answer and can't find it, hope you guys can help.

The get something on the screen you need to include self.config(width=700, heigh=800) (width and height totally arbitrary :)!) before self.pack() in class GUI and change all schermata into self (as you have defined the instance of GUI as the master frame).
I made the program put something on screen with the version below and I had to define some variables like screen_height, screen_width just so to prove the concept.
I also defined the method scrivere. Anyway it is rendering something so hopefully you can proceed. Good luck.
import tkinter as tk
class GUI(tk.Frame):
def __init__(self):
super(GUI, self).__init__()
self.config(width=700, height=500)
self.pack()
# self.bg()
# self.immagine()
self.testo()
self.statistiche()
self.inserimenti()
def bg(self):
load = Image.open("immagini\\background.png")
render = ImageTk.PhotoImage(load)
img = Label(self, image = render)
img.image = render
img.pack()
def immagine(self):
load = Image.open("immagini\\dn.png")
render = ImageTk.PhotoImage(load)
img = Label(self, image = render)
img.image = render
img.place( x = 10, y = 10 )
def testo(self):
self.testo = tk.Text(self, width = 110, height = 35, border = 5, bg = "black", fg ="white")
self.testo.place( x = 400, y = 20 )
def statistiche(self):
screen_height = 400
self.stats = tk.Text(self, width = 40, height = 10, border = 5, bg = "black", fg ="white")
self.stats.place( x = 10, y = (screen_height - 200))
def inserisci(self):
fraseInserita = self.inserimento.get()
self.scrivere(fraseInserita)
self.inserimento.delete('0', 'end')
def inserimenti(self):
large_font = ('calibri', 12)
screen_height = 400
screen_width = 600
self.inserimento = tk.Entry(self,font=large_font, width = 25, border = 5, bg = "black", fg ="white")
self.inserimento.place( x = 400, y = (screen_height - 100))
self.bottone = tk.Button(self, width = 30, height = 3, border = 5, text = "Inserisci", command = self.inserisci)
self.bottone.place( x = (screen_width - 300), y = (screen_height - 100))
def scrivere(self, frase):
print(' you need to define this function when button is pressed')
class Livelli():
def __init__(self):
pass
def cicloLivelli(self):
self.presentazione()
def presentazione(self):
print("Salve avventuriero, qual e' il tuo nome?")
if __name__ == "__main__":
a = GUI()
l = Livelli()
l.cicloLivelli()
a.mainloop()

Related

Switching between frames in tkinter, clock app

Good day, guys. I've got a problem with switching between frames in tkinter. I'm trying to make a clock app with time, stopwatch, timer and alarm. If I click on the first frame it's working, but if I select the second frame it's breaks. The previous frame just don't open because the second frame refreshing too fast (lbl.after). Can you help me with switching between frames?
from tkinter import *
from tkmacosx import *
from time import strftime
root = Tk()
root.title('Time')
root.geometry('500x500')
root.resizable(False, False)
counter = -1
running = False
def time_frame():
string = strftime("%H:%M:%S %A %d %Y")
frame_time = Frame(root, width = 480, height = 410, bg = 'gray70')
frame_time.place(x = 10, y = 80)
lbl1 = Label(frame_time, text = list, width = 32, height = 10, font = ('Arial', 25))
lbl1.place(x = 10, y = 80)
lbl1.config(text=string)
lbl1.after(1000, time_frame)
def stopwatch_frame():
frame_stopwatch = Frame(root, width = 480, height = 410, bg = 'gray70')
frame_stopwatch.place(x = 10, y = 80)
lbl2 = Label(frame_stopwatch, text = '0', width = 25, height = 5, font = ('Arial', 25))
lbl2.place(x = 10, y = 80)
start_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Start')
start_button.place(x = 10, y = 240)
stop_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Stop')
stop_button.place(x = 100, y = 240)
reset_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Reset')
reset_button.place(x = 190, y = 240)
lbl2.after(1, stopwatch_frame)
frame_top = Frame(root, width = 500, height = 70, bg = 'gray64')
frame_top.place(x = 0, y = 0)
time_btn = Button(frame_top, text = 'Time', width = 50, height = 50, command = time_frame)
time_btn.place(x = 10, y = 10)
stopwatch_btn = Button(frame_top, text = 'Stopwatch', width = 50, height = 50, font = ('Arial', 9), command = stopwatch_frame)
stopwatch_btn.place(x = 80, y = 10)
timer_btn = Button(frame_top, text = 'Timer', width = 50, height = 50)
timer_btn.place(x = 150, y = 10)
alarm_btn = Button(frame_top, text = 'Alarm', width = 50, height = 50)
alarm_btn.place(x = 220, y = 10)
root.mainloop()
Here is a simplified example of a clock and stop watch class.
import tkinter as tk
from tkmacosx import *
from time import strftime, process_time
class timer:
delaya = None
delayb = None
def __init__( self ):
self.root = tk.Tk()
self.root.title('Time')
self.buttona = tk.Button( self.root, text = 'clock', width = 40, command = self.setclock )
self.buttonb = tk.Button( self.root, text = 'watch', width = 40, command = self.setwatch )
self.buttona.grid( row=0, column=0, sticky='ew' )
self.buttonb.grid( row=0, column=1, sticky='ew' )
self.root.update_idletasks()
self.root.resizable( False, False )
def clock( self ):
self.root.after_cancel( self.delaya )
self.delaya = None
self.buttona[ 'text' ] = strftime("%H:%M:%S %A %d %Y")
self.delaya = self.root.after( 1000, self.clock )
def watch( self ):
self.root.after_cancel( self.delayb )
self.delayb = None
self.buttonb[ 'text' ] = f'{process_time():0.5f}'
self.delayb = self.root.after( 1, self.watch )
def setclock( self ):
if self.delaya == None:
self.delaya = self.root.after( 1000, self.clock )
else:
self.root.after_cancel( self.delaya )
self.delaya = None
self.buttona[ 'text' ] = 'clock'
def setwatch( self ):
if self.delayb == None:
self.delayb = self.root.after( 1, self.watch )
else:
self.root.after_cancel( self.delayb )
self.delayb = None
self.buttonb[ 'text' ] = 'watch'
if __name__ == '__main__':
device = timer( )
tk.mainloop()

Tkinter resize a rectange on Canvas using mouse

I want to create a simple GUI image labeler tool in Tkinter for my research project. Currently, I have working code that can load images from the directory and allows me to draw multiple bounding boxes. I want to modify the code such that I can resize the rectangle BB using a mouse when I click on it. I have searched online and couldn't find any resources to do that. Can someone help me understand how to do that?
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
from PIL import Image, ImageTk
import os
import glob
import random
# colors for the bboxes
COLORS = ['red', 'blue','pink', 'cyan', 'green', 'black']
# image sizes for the examples
SIZE = 256, 256
class LabelTool():
def __init__(self, master):
# set up the main frame
self.parent = master
self.parent.title("LabelTool")
self.frame = Frame(self.parent)
self.frame.pack(fill=BOTH, expand=1)
self.parent.resizable(width = FALSE, height = FALSE)
# initialize global state
self.imageDir = ''
self.imageList= []
self.egDir = ''
self.egList = []
self.outDir = ''
self.cur = 0
self.total = 0
self.category = 0
self.imagename = ''
self.labelfilename = ''
self.tkimg = None
self.currentLabelclass = ''
self.cla_can_temp = []
self.classcandidate_filename = 'class.txt'
# initialize mouse state
self.STATE = {}
self.STATE['click'] = 0
self.STATE['x'], self.STATE['y'] = 0, 0
# reference to bbox
self.bboxIdList = []
self.bboxId = None
self.bboxList = []
self.hl = None
self.vl = None
self.srcDirBtn = Button(self.frame, text="Image input folder", command=self.selectSrcDir)
self.srcDirBtn.grid(row=0, column=0)
# input image dir entry
self.svSourcePath = StringVar()
self.entrySrc = Entry(self.frame, textvariable=self.svSourcePath)
self.entrySrc.grid(row=0, column=1, sticky=W+E)
self.svSourcePath.set(os.getcwd())
# load button
self.ldBtn = Button(self.frame, text="Load Dir", command=self.loadDir)
self.ldBtn.grid(row=0, column=2, rowspan=2, columnspan=2, padx=2, pady=2, ipadx=5, ipady=5)
# label file save dir button
self.desDirBtn = Button(self.frame, text="Label output folder", command=self.selectDesDir)
self.desDirBtn.grid(row=1, column=0)
# label file save dir entry
self.svDestinationPath = StringVar()
self.entryDes = Entry(self.frame, textvariable=self.svDestinationPath)
self.entryDes.grid(row=1, column=1, sticky=W+E)
self.svDestinationPath.set(os.path.join(os.getcwd(),"Labels"))
# main panel for labeling
self.mainPanel = Canvas(self.frame, cursor='tcross')
self.mainPanel.bind("<Button-1>", self.mouseClick)
self.mainPanel.bind("<Motion>", self.mouseMove)
self.mainPanel.grid(row = 2, column = 1, rowspan = 4, sticky = W+N)
# showing bbox info & delete bbox
self.lb1 = Label(self.frame, text = 'Bounding boxes:')
self.lb1.grid(row = 3, column = 2, sticky = W+N)
self.listbox = Listbox(self.frame, width = 22, height = 12)
self.listbox.grid(row = 4, column = 2, sticky = N+S)
# control panel for image navigation
self.ctrPanel = Frame(self.frame)
self.ctrPanel.grid(row = 6, column = 1, columnspan = 2, sticky = W+E)
self.progLabel = Label(self.ctrPanel, text = "Progress: / ")
self.progLabel.pack(side = LEFT, padx = 5)
self.tmpLabel = Label(self.ctrPanel, text = "Go to Image No.")
self.tmpLabel.pack(side = LEFT, padx = 5)
self.idxEntry = Entry(self.ctrPanel, width = 5)
self.idxEntry.pack(side = LEFT)
# display mouse position
self.disp = Label(self.ctrPanel, text='')
self.disp.pack(side = RIGHT)
self.frame.columnconfigure(1, weight = 1)
self.frame.rowconfigure(4, weight = 1)
def selectSrcDir(self):
path = filedialog.askdirectory(title="Select image source folder", initialdir=self.svSourcePath.get())
self.svSourcePath.set(path)
return
def selectDesDir(self):
path = filedialog.askdirectory(title="Select label output folder", initialdir=self.svDestinationPath.get())
self.svDestinationPath.set(path)
return
def loadDir(self):
self.parent.focus()
# get image list
#self.imageDir = os.path.join(r'./Images', '%03d' %(self.category))
self.imageDir = self.svSourcePath.get()
if not os.path.isdir(self.imageDir):
messagebox.showerror("Error!", message = "The specified dir doesn't exist!")
return
extlist = ["*.JPEG", "*.jpeg", "*JPG", "*.jpg", "*.PNG", "*.png", "*.BMP", "*.bmp"]
for e in extlist:
filelist = glob.glob(os.path.join(self.imageDir, e))
self.imageList.extend(filelist)
#self.imageList = glob.glob(os.path.join(self.imageDir, '*.JPEG'))
if len(self.imageList) == 0:
print('No .JPEG images found in the specified dir!')
return
# default to the 1st image in the collection
self.cur = 1
self.total = len(self.imageList)
# set up output dir
#self.outDir = os.path.join(r'./Labels', '%03d' %(self.category))
self.outDir = self.svDestinationPath.get()
if not os.path.exists(self.outDir):
os.mkdir(self.outDir)
self.loadImage()
print('%d images loaded from %s' %(self.total, self.imageDir))
def loadImage(self):
# load image
imagepath = self.imageList[self.cur - 1]
self.img = Image.open(imagepath)
size = self.img.size
self.factor = max(size[0]/1000, size[1]/1000., 1.)
self.img = self.img.resize((int(size[0]/self.factor), int(size[1]/self.factor)))
self.tkimg = ImageTk.PhotoImage(self.img)
self.mainPanel.config(width = max(self.tkimg.width(), 400), height = max(self.tkimg.height(), 400))
self.mainPanel.create_image(0, 0, image = self.tkimg, anchor=NW)
self.progLabel.config(text = "%04d/%04d" %(self.cur, self.total))
# load labels
self.clearBBox()
#self.imagename = os.path.split(imagepath)[-1].split('.')[0]
fullfilename = os.path.basename(imagepath)
self.imagename, _ = os.path.splitext(fullfilename)
labelname = self.imagename + '.txt'
self.labelfilename = os.path.join(self.outDir, labelname)
bbox_cnt = 0
if os.path.exists(self.labelfilename):
with open(self.labelfilename) as f:
for (i, line) in enumerate(f):
if i == 0:
bbox_cnt = int(line.strip())
continue
#tmp = [int(t.strip()) for t in line.split()]
tmp = line.split()
tmp[0] = int(int(tmp[0])/self.factor)
tmp[1] = int(int(tmp[1])/self.factor)
tmp[2] = int(int(tmp[2])/self.factor)
tmp[3] = int(int(tmp[3])/self.factor)
self.bboxList.append(tuple(tmp))
color_index = (len(self.bboxList)-1) % len(COLORS)
tmpId = self.mainPanel.create_rectangle(tmp[0], tmp[1], \
tmp[2], tmp[3], \
width = 2, \
outline = COLORS[color_index])
#outline = COLORS[(len(self.bboxList)-1) % len(COLORS)])
self.bboxIdList.append(tmpId)
self.listbox.insert(END, '%s : (%d, %d) -> (%d, %d)' %(tmp[4], tmp[0], tmp[1], tmp[2], tmp[3]))
self.listbox.itemconfig(len(self.bboxIdList) - 1, fg = COLORS[color_index])
#self.listbox.itemconfig(len(self.bboxIdList) - 1, fg = COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
def mouseClick(self, event):
if self.STATE['click'] == 0:
self.STATE['x'], self.STATE['y'] = event.x, event.y
else:
x1, x2 = min(self.STATE['x'], event.x), max(self.STATE['x'], event.x)
y1, y2 = min(self.STATE['y'], event.y), max(self.STATE['y'], event.y)
self.bboxList.append((x1, y1, x2, y2, self.currentLabelclass))
self.bboxIdList.append(self.bboxId)
self.bboxId = None
self.listbox.insert(END, '%s : (%d, %d) -> (%d, %d)' %(self.currentLabelclass, x1, y1, x2, y2))
self.listbox.itemconfig(len(self.bboxIdList) - 1, fg = COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
self.STATE['click'] = 1 - self.STATE['click']
def mouseMove(self, event):
self.disp.config(text = 'x: %d, y: %d' %(event.x, event.y))
if self.tkimg:
if self.hl:
self.mainPanel.delete(self.hl)
self.hl = self.mainPanel.create_line(0, event.y, self.tkimg.width(), event.y, width = 2)
if self.vl:
self.mainPanel.delete(self.vl)
self.vl = self.mainPanel.create_line(event.x, 0, event.x, self.tkimg.height(), width = 2)
if 1 == self.STATE['click']:
if self.bboxId:
self.mainPanel.delete(self.bboxId)
COLOR_INDEX = len(self.bboxIdList) % len(COLORS)
self.bboxId = self.mainPanel.create_rectangle(self.STATE['x'], self.STATE['y'], \
event.x, event.y, \
width = 2, \
outline = COLORS[len(self.bboxList) % len(COLORS)])
def setClass(self):
self.currentLabelclass = self.classcandidate.get()
print('set label class to : %s' % self.currentLabelclass)
if __name__ == '__main__':
root = Tk()
tool = LabelTool(root)
root.resizable(width = True, height = True)
root.mainloop()
That's hardly "minimal" ... it's still over 200 lines long. I don't want to sort through it but I'll make a minimal example to show you how to bind to a canvas item:
import tkinter as tk
from functools import partial
class DrawShapes(tk.Canvas):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
image = self.create_rectangle(0, 0, 400, 300, width=5, fill='green')
self.tag_bind(image, '<Button-1>', self.on_click)
self.tag_bind(image, '<Button1-Motion>', self.on_motion)
def on_click(self, event):
"""fires when user clicks on the background ... creates a new rectangle"""
self.start = event.x, event.y
self.current = self.create_rectangle(*self.start, *self.start, width=5)
self.tag_bind(self.current, '<Button-1>', partial(self.on_click_rectangle, self.current))
self.tag_bind(self.current, '<Button1-Motion>', self.on_motion)
def on_click_rectangle(self, tag, event):
"""fires when the user clicks on a rectangle ... edits the clicked on rectange"""
self.current = tag
x1, y1, x2, y2 = self.coords(tag)
if abs(event.x-x1) < abs(event.x-x2):
# opposing side was grabbed; swap the anchor and mobile side
x1, x2 = x2, x1
if abs(event.y-y1) < abs(event.y-y2):
y1, y2 = y2, y1
self.start = x1, y1
def on_motion(self, event):
"""fires when the user drags the mouse ... resizes currently active rectangle"""
self.coords(self.current, *self.start, event.x, event.y)
def main():
c = DrawShapes()
c.pack()
c.mainloop()
if __name__ == '__main__':
main()

What's making my tkinter graphics so slow?

I'm trying to implement GUI editing tool by python.
I implemented preliminary code to test how work well.
However, it is too slow to use even if small sample case.
My code is below:
import numpy as np
from tkinter import *
nx=10
ny=8
world=np.zeros((ny,nx))
world[:,3]=1
world[1,:]=1
# Tkinter ========
root = Tk()
root.title("make Land/Sea mask")
root.resizable(0,0)
menu_ROOT = Menu(root)
root.configure(menu = menu_ROOT)
menu_GAME = Menu(menu_ROOT, tearoff = False)
menu_ROOT.add_cascade(label = 'Menu', under = 4, menu = menu_GAME)
###Frame objects###
root_frame = Frame(root, relief = 'groove', borderwidth = 5, bg = 'LightGray')
game_frame = Frame(root_frame, relief = 'sunken', borderwidth = 3, bg = 'LightGray')
root_frame.pack()
game_frame.pack(pady = 5, padx = 5)
def create_map(world,nx,ny):
c = 0
frame_list = []
for j in np.arange(ny):
for i in np.arange(nx):
if world[j,i]==1:
frame = Frame(game_frame, width = 20, height = 20, bd = 0.5, relief = 'groove', bg = 'yellow green')
else:
frame = Frame(game_frame, width = 20, height = 20, bd = 0.5, relief = 'ridge', bg = 'pale green')
frame.num = c
frame_list.append(frame)
frame.grid(row=j, column=i)
c += 1
create_map(world,nx,ny)
root.mainloop()
In this case, I set 10x8 matrix, but I finally will use 360x180 matrix and it may takes an enormous amount of time to draw.
Does anyone know how improve this code?
You shouldn't use Tkinter for this purpose.
Here's an example in PyGame, which in my opinion is pretty easy to learn:
import sys
import time
import pygame
import numpy as np
DISPLAY_SIZE = 840, 680
def generate_map(nx, ny):
return np.rint(np.random.rand(ny, nx))
def render_map(world, display, selected_img, unselected_img, img_size):
for j in np.arange(world.shape[0]):
for i in np.arange(world.shape[1]):
if world[j, i] == 1:
display.blit(selected_img, (i * img_size[0], j * img_size[0]))
else:
display.blit(unselected_img, (i * img_size[0], j * img_size[0]))
display = pygame.display.set_mode(DISPLAY_SIZE, pygame.HWSURFACE | pygame.DOUBLEBUF)
selected = pygame.image.load("selected_cell.png")
unselected = pygame.image.load("unselected_cell.png")
img_size = selected.get_size() # Assuming both images are the same size
clock = pygame.time.Clock()
world = generate_map(26, 21)
fps_counter_time = time.perf_counter()
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
render_map(world, display, selected, unselected, img_size)
pygame.display.flip()
if (time.perf_counter() - fps_counter_time) > 0.1:
pygame.display.set_caption("FPS: %.4f" % (clock.get_fps(),))
fps_counter_time = time.perf_counter()

How to move Tkinter widget precisely. Place method not working

Here's the code so far:
# -*- coding: utf-8 -*-
from Tkinter import *
#Creates game window
master = Tk()
master.geometry("640x480")
master.resizable(width = False, height = False)
master.title("YeeHaw Poker")
#Divides window into subsections
menuFrame = Frame(master, bg = "black", height = 60)
menuFrame.pack(fill = X, side = TOP)
tableFrame = Frame(master, highlightbackground = "black", highlightthickness = 4)
tableFrame.pack(fill = BOTH, expand = True)
optionsFrame = Frame(master, bg = "black", height = 100)
optionsFrame.pack(fill = X, side = BOTTOM)
#Draws poker table decorations
tableDecorations = Canvas(tableFrame, bg = "#771427", highlightthickness = 0)
tableDecorations.pack(fill = BOTH, expand = True)
#Renders window thus far so that dimensions can be found
master.update()
tWidth = tableDecorations.winfo_width()
tHeight = tableDecorations.winfo_height()
#Main edge
gap = 10
tableDecorations.create_rectangle(gap, gap, tWidth - gap, tHeight - gap, fill ="#277714", width = 4)
#Table outline
gap = 30
tableDecorations.create_rectangle(gap, gap, tWidth - gap, tHeight - gap, outline = "#35a31b", width = 2)
#Card outline coordinates
cardNum = 5
cardSize = 20
cardHeight = cardSize * 3.5
cardWidth = cardSize * 2.5
cardSpace = 10
cardTop = tHeight / 4
cardLeft = (tWidth - (cardNum * (cardWidth + cardSpace))) / 2
cardY1 = cardTop + cardHeight
cardY2 = cardTop
cardX1 = [0 for i in range(0, cardNum)]
cardX2 = [0 for i in range(0, cardNum)]
suit = [0 for i in range(0, cardNum)]
for i in range(0, cardNum):
cardX1[i] = cardLeft + (i * (cardWidth + cardSpace))
cardX2[i] = cardX1[i] + cardWidth
suit[i] = Label(tableDecorations, text = "", bg = "white", font = (None, 50))
suit[i].place(x = 5000, y = 5000)
#Draws specified card in specified place
def drawCard(pos, type, pip):
if type == "empty":
tableDecorations.create_rectangle(cardX1[pos], cardY1, cardX2[pos], cardY2, outline = "#35a31b", width = 2)
suit[pos].pack_forget()
else:
tableDecorations.create_rectangle(cardX1[pos], cardY1, cardX2[pos], cardY2, fill = "white", outline = "grey", width = 1)
if type == "diamond":
suit[pos].config(text = "♦", fg = "red")
elif type == "heart":
suit[pos].config(text = "♥", fg = "red")
elif type == "spade":
suit[pos].config(text = "♠", fg = "black")
elif type == "club":
suit[pos].config(text = "♣", fg = "black")
suit[pos].pack()
#Creates new table
def newTable():
for i in range(0, cardNum):
drawCard(i, "diamond", 0)
newTable()
master.mainloop()
However this doesn't move the labels with the diamonds in at all, as shown here:
It's infuriating...
I'm wanting the diamond to appear on each individual card, but clearly that's not happening here...
Any ideas?

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

Categories